8193085: Vectorize the nio Buffer equals and compareTo implementations
authorpsandoz
Wed, 20 Dec 2017 09:14:52 -0800
changeset 48356 29e165bdf669
parent 48355 4944950606ef
child 48357 63fb11c1550d
child 48475 18fb03624696
8193085: Vectorize the nio Buffer equals and compareTo implementations Reviewed-by: alanb
src/hotspot/share/classfile/vmSymbols.hpp
src/java.base/share/classes/java/nio/Bits.java
src/java.base/share/classes/java/nio/Buffer.java
src/java.base/share/classes/java/nio/BufferMismatch.java
src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template
src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template
src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template
src/java.base/share/classes/java/nio/StringCharBuffer.java
src/java.base/share/classes/java/nio/X-Buffer.java.template
src/java.base/share/classes/java/util/Arrays.java
src/java.base/share/classes/java/util/ArraysSupport.java
src/java.base/share/classes/jdk/internal/util/ArraysSupport.java
test/jdk/java/nio/Buffer/EqualsCompareTest.java
--- a/src/hotspot/share/classfile/vmSymbols.hpp	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/hotspot/share/classfile/vmSymbols.hpp	Wed Dec 20 09:14:52 2017 -0800
@@ -996,8 +996,8 @@
    do_name(     montgomerySquare_name,                             "implMontgomerySquare")                              \
    do_signature(montgomerySquare_signature,                        "([I[IIJ[I)[I")                                      \
                                                                                                                         \
-  do_class(java_util_ArraysSupport, "java/util/ArraysSupport")                                                          \
-  do_intrinsic(_vectorizedMismatch, java_util_ArraysSupport, vectorizedMismatch_name, vectorizedMismatch_signature, F_S)\
+  do_class(jdk_internal_util_ArraysSupport, "jdk/internal/util/ArraysSupport")                                                          \
+  do_intrinsic(_vectorizedMismatch, jdk_internal_util_ArraysSupport, vectorizedMismatch_name, vectorizedMismatch_signature, F_S)\
    do_name(vectorizedMismatch_name, "vectorizedMismatch")                                                               \
    do_signature(vectorizedMismatch_signature, "(Ljava/lang/Object;JLjava/lang/Object;JII)I")                            \
                                                                                                                         \
--- a/src/java.base/share/classes/java/nio/Bits.java	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/Bits.java	Wed Dec 20 09:14:52 2017 -0800
@@ -63,38 +63,38 @@
 
     // -- Unsafe access --
 
-    private static final Unsafe unsafe = Unsafe.getUnsafe();
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 
     static Unsafe unsafe() {
-        return unsafe;
+        return UNSAFE;
     }
 
 
     // -- Processor and memory-system properties --
 
-    private static final ByteOrder byteOrder
-        = unsafe.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
+    private static final ByteOrder BYTE_ORDER
+        = UNSAFE.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
 
     static ByteOrder byteOrder() {
-        return byteOrder;
+        return BYTE_ORDER;
     }
 
-    private static int pageSize = -1;
+    private static int PAGE_SIZE = -1;
 
     static int pageSize() {
-        if (pageSize == -1)
-            pageSize = unsafe().pageSize();
-        return pageSize;
+        if (PAGE_SIZE == -1)
+            PAGE_SIZE = unsafe().pageSize();
+        return PAGE_SIZE;
     }
 
     static int pageCount(long size) {
         return (int)(size + (long)pageSize() - 1L) / pageSize();
     }
 
-    private static boolean unaligned = unsafe.unalignedAccess();
+    private static boolean UNALIGNED = UNSAFE.unalignedAccess();
 
     static boolean unaligned() {
-        return unaligned;
+        return UNALIGNED;
     }
 
 
@@ -103,11 +103,11 @@
     // A user-settable upper limit on the maximum amount of allocatable
     // direct buffer memory.  This value may be changed during VM
     // initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
-    private static volatile long maxMemory = VM.maxDirectMemory();
-    private static final AtomicLong reservedMemory = new AtomicLong();
-    private static final AtomicLong totalCapacity = new AtomicLong();
-    private static final AtomicLong count = new AtomicLong();
-    private static volatile boolean memoryLimitSet;
+    private static volatile long MAX_MEMORY = VM.maxDirectMemory();
+    private static final AtomicLong RESERVED_MEMORY = new AtomicLong();
+    private static final AtomicLong TOTAL_CAPACITY = new AtomicLong();
+    private static final AtomicLong COUNT = new AtomicLong();
+    private static volatile boolean MEMORY_LIMIT_SET;
 
     // max. number of sleeps during try-reserving with exponentially
     // increasing delay before throwing OutOfMemoryError:
@@ -120,9 +120,9 @@
     // which a process may access.  All sizes are specified in bytes.
     static void reserveMemory(long size, int cap) {
 
-        if (!memoryLimitSet && VM.initLevel() >= 1) {
-            maxMemory = VM.maxDirectMemory();
-            memoryLimitSet = true;
+        if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) {
+            MAX_MEMORY = VM.maxDirectMemory();
+            MEMORY_LIMIT_SET = true;
         }
 
         // optimist!
@@ -200,10 +200,10 @@
         // actual memory usage, which will differ when buffers are page
         // aligned.
         long totalCap;
-        while (cap <= maxMemory - (totalCap = totalCapacity.get())) {
-            if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) {
-                reservedMemory.addAndGet(size);
-                count.incrementAndGet();
+        while (cap <= MAX_MEMORY - (totalCap = TOTAL_CAPACITY.get())) {
+            if (TOTAL_CAPACITY.compareAndSet(totalCap, totalCap + cap)) {
+                RESERVED_MEMORY.addAndGet(size);
+                COUNT.incrementAndGet();
                 return true;
             }
         }
@@ -213,9 +213,9 @@
 
 
     static void unreserveMemory(long size, int cap) {
-        long cnt = count.decrementAndGet();
-        long reservedMem = reservedMemory.addAndGet(-size);
-        long totalCap = totalCapacity.addAndGet(-cap);
+        long cnt = COUNT.decrementAndGet();
+        long reservedMem = RESERVED_MEMORY.addAndGet(-size);
+        long totalCap = TOTAL_CAPACITY.addAndGet(-cap);
         assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0;
     }
 
@@ -234,15 +234,15 @@
                         }
                         @Override
                         public long getCount() {
-                            return Bits.count.get();
+                            return Bits.COUNT.get();
                         }
                         @Override
                         public long getTotalCapacity() {
-                            return Bits.totalCapacity.get();
+                            return Bits.TOTAL_CAPACITY.get();
                         }
                         @Override
                         public long getMemoryUsed() {
-                            return Bits.reservedMemory.get();
+                            return Bits.RESERVED_MEMORY.get();
                         }
                     };
                 }
--- a/src/java.base/share/classes/java/nio/Buffer.java	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/Buffer.java	Wed Dec 20 09:14:52 2017 -0800
@@ -26,6 +26,7 @@
 package java.nio;
 
 import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.misc.Unsafe;
 
 import java.util.Spliterator;
 
@@ -181,6 +182,8 @@
  */
 
 public abstract class Buffer {
+    // Cached unsafe-access object
+    static final Unsafe UNSAFE = Bits.unsafe();
 
     /**
      * The characteristics of Spliterators that traverse and split elements
@@ -617,6 +620,14 @@
     // -- Package-private methods for bounds checking, etc. --
 
     /**
+     *
+     * @return the base reference, paired with the address
+     * field, which in combination can be used for unsafe access into a heap
+     * buffer or direct byte buffer (and views of).
+     */
+    abstract Object base();
+
+    /**
      * Checks the current position against the limit, throwing a {@link
      * BufferUnderflowException} if it is not smaller than the limit, and then
      * increments the position.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/nio/BufferMismatch.java	Wed Dec 20 09:14:52 2017 -0800
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.nio;
+
+import jdk.internal.util.ArraysSupport;
+
+/**
+ * Mismatch methods for buffers
+ */
+final class BufferMismatch {
+
+    static int mismatch(ByteBuffer a, int aOff, ByteBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 7) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + aOff,
+                    b.base(), b.address + bOff,
+                    length,
+                    ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
+            if (i >= 0) return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a.get(aOff + i) != b.get(bOff + i))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(CharBuffer a, int aOff, CharBuffer b, int bOff, int length) {
+        int i = 0;
+        // Ensure only heap or off-heap buffer instances use the
+        // vectorized mismatch. If either buffer is a StringCharBuffer
+        // (order is null) then the slow path is taken
+        if (length > 3 && a.charRegionOrder() == b.charRegionOrder()
+            && a.charRegionOrder() != null && b.charRegionOrder() != null) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE);
+            if (i >= 0) return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a.get(aOff + i) != b.get(bOff + i))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(ShortBuffer a, int aOff, ShortBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 3 && a.order() == b.order()) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE);
+            if (i >= 0) return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a.get(aOff + i) != b.get(bOff + i))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(IntBuffer a, int aOff, IntBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 1 && a.order() == b.order()) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE);
+            if (i >= 0) return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a.get(aOff + i) != b.get(bOff + i))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(FloatBuffer a, int aOff, FloatBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 1 && a.order() == b.order()) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE);
+            // Mismatched
+            if (i >= 0) {
+                // Check if mismatch is not associated with two NaN values; and
+                // is not associated with +0 and -0
+                float av = a.get(aOff + i);
+                float bv = b.get(bOff + i);
+                if (av != bv && (!Float.isNaN(av) || !Float.isNaN(bv)))
+                    return i;
+
+                // Fall back to slow mechanism
+                // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
+                // However, requires that returned value be relative to input ranges
+                i++;
+            }
+            // Matched
+            else {
+                i = length - ~i;
+            }
+        }
+        for (; i < length; i++) {
+            float av = a.get(aOff + i);
+            float bv = b.get(bOff + i);
+            if (av != bv && (!Float.isNaN(av) || !Float.isNaN(bv)))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(LongBuffer a, int aOff, LongBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 0 && a.order() == b.order()) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE);
+            return i >= 0 ? i : -1;
+        }
+        for (; i < length; i++) {
+            if (a.get(aOff + i) != b.get(bOff + i))
+                return i;
+        }
+        return -1;
+    }
+
+    static int mismatch(DoubleBuffer a, int aOff, DoubleBuffer b, int bOff, int length) {
+        int i = 0;
+        if (length > 0 && a.order() == b.order()) {
+            i = ArraysSupport.vectorizedMismatch(
+                    a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE),
+                    b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE),
+                    length,
+                    ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE);
+            // Mismatched
+            if (i >= 0) {
+                // Check if mismatch is not associated with two NaN values; and
+                // is not associated with +0 and -0
+                double av = a.get(aOff + i);
+                double bv = b.get(bOff + i);
+                if (av != bv && (!Double.isNaN(av) || !Double.isNaN(bv)))
+                    return i;
+
+                // Fall back to slow mechanism
+                // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
+                // However, requires that returned value be relative to input ranges
+                i++;
+            }
+            // Matched
+            else {
+                return -1;
+            }
+        }
+        for (; i < length; i++) {
+            double av = a.get(aOff + i);
+            double bv = b.get(bOff + i);
+            if (av != bv && (!Double.isNaN(av) || !Double.isNaN(bv)))
+                return i;
+        }
+        return -1;
+    }
+}
--- a/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template	Wed Dec 20 09:14:52 2017 -0800
@@ -36,9 +36,6 @@
 
 #if[rw]
 
-    // Cached unsafe-access object
-    private static final Unsafe unsafe = Bits.unsafe();
-
     protected final ByteBuffer bb;
 
 #end[rw]
@@ -74,6 +71,11 @@
 #end[rw]
     }
 
+    @Override
+    Object base() {
+        return bb.hb;
+    }
+
     public $Type$Buffer slice() {
         int pos = this.position();
         int lim = this.limit();
@@ -117,20 +119,20 @@
     }
 
     public $type$ get() {
-        $memtype$ x = unsafe.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
+        $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
             {#if[boB]?true:false});
         return $fromBits$(x);
     }
 
     public $type$ get(int i) {
-        $memtype$ x = unsafe.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
+        $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
             {#if[boB]?true:false});
         return $fromBits$(x);
     }
 
 #if[streamableType]
    $type$ getUnchecked(int i) {
-        $memtype$ x = unsafe.get$Memtype$Unaligned(bb.hb, byteOffset(i),
+        $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(i),
             {#if[boB]?true:false});
         return $fromBits$(x);
     }
@@ -141,7 +143,7 @@
     public $Type$Buffer put($type$ x) {
 #if[rw]
         $memtype$ y = $toBits$(x);
-        unsafe.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
+        UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
             {#if[boB]?true:false});
         return this;
 #else[rw]
@@ -152,7 +154,7 @@
     public $Type$Buffer put(int i, $type$ x) {
 #if[rw]
         $memtype$ y = $toBits$(x);
-        unsafe.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
+        UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
             {#if[boB]?true:false});
         return this;
 #else[rw]
@@ -241,4 +243,9 @@
 #end[boL]
     }
 
+#if[char]
+    ByteOrder charRegionOrder() {
+        return order();
+    }
+#end[char]
 }
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template	Wed Dec 20 09:14:52 2017 -0800
@@ -32,7 +32,7 @@
 #if[rw]
 
     private $type$ get$Type$(long a) {
-        $memtype$ x = unsafe.get$Memtype$Unaligned(null, a, bigEndian);
+        $memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian);
         return $fromBits$(x);
     }
 
@@ -49,7 +49,7 @@
     private ByteBuffer put$Type$(long a, $type$ x) {
 #if[rw]
         $memtype$ y = $toBits$(x);
-        unsafe.put$Memtype$Unaligned(null, a, y, bigEndian);
+        UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -81,7 +81,7 @@
         int rem = (off <= lim ? lim - off : 0);
 
         int size = rem >> $LG_BYTES_PER_VALUE$;
-        if (!unaligned && ((address + off) % $BYTES_PER_VALUE$ != 0)) {
+        if (!UNALIGNED && ((address + off) % $BYTES_PER_VALUE$ != 0)) {
             return (bigEndian
                     ? ($Type$Buffer)(new ByteBufferAs$Type$Buffer$RW$B(this,
                                                                        -1,
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template	Wed Dec 20 09:14:52 2017 -0800
@@ -28,7 +28,6 @@
 package java.nio;
 
 import java.io.FileDescriptor;
-import jdk.internal.misc.Unsafe;
 import jdk.internal.misc.VM;
 import jdk.internal.ref.Cleaner;
 import sun.nio.ch.DirectBuffer;
@@ -45,14 +44,11 @@
 
 #if[rw]
 
-    // Cached unsafe-access object
-    protected static final Unsafe unsafe = Bits.unsafe();
-
     // Cached array base offset
-    private static final long arrayBaseOffset = (long)unsafe.arrayBaseOffset($type$[].class);
+    private static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset($type$[].class);
 
     // Cached unaligned-access capability
-    protected static final boolean unaligned = Bits.unaligned();
+    protected static final boolean UNALIGNED = Bits.unaligned();
 
     // Base address, used in all indexing calculations
     // NOTE: moved up to Buffer.java for speed in JNI GetDirectBufferAddress
@@ -73,8 +69,6 @@
         implements Runnable
     {
 
-        private static Unsafe unsafe = Unsafe.getUnsafe();
-
         private long address;
         private long size;
         private int capacity;
@@ -91,7 +85,7 @@
                 // Paranoia
                 return;
             }
-            unsafe.freeMemory(address);
+            UNSAFE.freeMemory(address);
             address = 0;
             Bits.unreserveMemory(size, capacity);
         }
@@ -124,12 +118,12 @@
 
         long base = 0;
         try {
-            base = unsafe.allocateMemory(size);
+            base = UNSAFE.allocateMemory(size);
         } catch (OutOfMemoryError x) {
             Bits.unreserveMemory(size, cap);
             throw x;
         }
-        unsafe.setMemory(base, size, (byte) 0);
+        UNSAFE.setMemory(base, size, (byte) 0);
         if (pa && (base % ps != 0)) {
             // Round up to page boundary
             address = base + ps - (base & (ps - 1));
@@ -206,6 +200,11 @@
 #end[rw]
     }
 
+    @Override
+    Object base() {
+        return null;
+    }
+
     public $Type$Buffer slice() {
         int pos = this.position();
         int lim = this.limit();
@@ -258,16 +257,16 @@
     }
 
     public $type$ get() {
-        return $fromBits$($swap$(unsafe.get$Swaptype$(ix(nextGetIndex()))));
+        return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex()))));
     }
 
     public $type$ get(int i) {
-        return $fromBits$($swap$(unsafe.get$Swaptype$(ix(checkIndex(i)))));
+        return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i)))));
     }
 
 #if[streamableType]
     $type$ getUnchecked(int i) {
-        return $fromBits$($swap$(unsafe.get$Swaptype$(ix(i))));
+        return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(i))));
     }
 #end[streamableType]
 
@@ -282,10 +281,10 @@
             if (length > rem)
                 throw new BufferUnderflowException();
 
-            long dstOffset = arrayBaseOffset + ((long)offset << $LG_BYTES_PER_VALUE$);
+            long dstOffset = ARRAY_BASE_OFFSET + ((long)offset << $LG_BYTES_PER_VALUE$);
 #if[!byte]
             if (order() != ByteOrder.nativeOrder())
-                unsafe.copySwapMemory(null,
+                UNSAFE.copySwapMemory(null,
                                       ix(pos),
                                       dst,
                                       dstOffset,
@@ -293,7 +292,7 @@
                                       (long)1 << $LG_BYTES_PER_VALUE$);
             else
 #end[!byte]
-                unsafe.copyMemory(null,
+                UNSAFE.copyMemory(null,
                                   ix(pos),
                                   dst,
                                   dstOffset,
@@ -312,7 +311,7 @@
 
     public $Type$Buffer put($type$ x) {
 #if[rw]
-        unsafe.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
+        UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -321,7 +320,7 @@
 
     public $Type$Buffer put(int i, $type$ x) {
 #if[rw]
-        unsafe.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
+        UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -347,7 +346,7 @@
 
             if (srem > rem)
                 throw new BufferOverflowException();
-            unsafe.copyMemory(sb.ix(spos), ix(pos), (long)srem << $LG_BYTES_PER_VALUE$);
+            UNSAFE.copyMemory(sb.ix(spos), ix(pos), (long)srem << $LG_BYTES_PER_VALUE$);
             sb.position(spos + srem);
             position(pos + srem);
         } else if (src.hb != null) {
@@ -380,10 +379,10 @@
             if (length > rem)
                 throw new BufferOverflowException();
 
-            long srcOffset = arrayBaseOffset + ((long)offset << $LG_BYTES_PER_VALUE$);
+            long srcOffset = ARRAY_BASE_OFFSET + ((long)offset << $LG_BYTES_PER_VALUE$);
 #if[!byte]
             if (order() != ByteOrder.nativeOrder())
-                unsafe.copySwapMemory(src,
+                UNSAFE.copySwapMemory(src,
                                       srcOffset,
                                       null,
                                       ix(pos),
@@ -391,7 +390,7 @@
                                       (long)1 << $LG_BYTES_PER_VALUE$);
             else
 #end[!byte]
-                unsafe.copyMemory(src,
+                UNSAFE.copyMemory(src,
                                   srcOffset,
                                   null,
                                   ix(pos),
@@ -413,7 +412,7 @@
         assert (pos <= lim);
         int rem = (pos <= lim ? lim - pos : 0);
 
-        unsafe.copyMemory(ix(pos), ix(0), (long)rem << $LG_BYTES_PER_VALUE$);
+        UNSAFE.copyMemory(ix(pos), ix(0), (long)rem << $LG_BYTES_PER_VALUE$);
         position(rem);
         limit(capacity());
         discardMark();
@@ -490,17 +489,22 @@
 
 #end[!byte]
 
+#if[char]
+    ByteOrder charRegionOrder() {
+        return order();
+    }
+#end[char]
 
 
 #if[byte]
 
     byte _get(int i) {                          // package-private
-        return unsafe.getByte(address + i);
+        return UNSAFE.getByte(address + i);
     }
 
     void _put(int i, byte b) {                  // package-private
 #if[rw]
-        unsafe.putByte(address + i, b);
+        UNSAFE.putByte(address + i, b);
 #else[rw]
         throw new ReadOnlyBufferException();
 #end[rw]
--- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template	Wed Dec 20 09:14:52 2017 -0800
@@ -27,8 +27,6 @@
 
 package java.nio;
 
-import jdk.internal.misc.Unsafe;
-
 /**
 #if[rw]
  * A read/write Heap$Type$Buffer.
@@ -43,6 +41,11 @@
 class Heap$Type$Buffer$RW$
     extends {#if[ro]?Heap}$Type$Buffer
 {
+    // Cached array base offset
+    private static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset($type$[].class);
+
+    // Cached array base offset
+    private static final long ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale($type$[].class);
 
     // For speed these fields are actually declared in X-Buffer;
     // these declarations are here as documentation
@@ -53,16 +56,6 @@
 #end[rw]
     */
 
-#if[byte]
-
-    // Cached unsafe-access object
-    private static final Unsafe unsafe = Bits.unsafe();
-
-    // Cached array base offset
-    private static final long arrayBaseOffset = unsafe.arrayBaseOffset($type$[].class);
-
-#end[byte]
-
     Heap$Type$Buffer$RW$(int cap, int lim) {            // package-private
 #if[rw]
         super(-1, 0, lim, cap, new $type$[cap], 0);
@@ -70,13 +63,11 @@
         hb = new $type$[cap];
         offset = 0;
         */
+        this.address = ARRAY_BASE_OFFSET;
 #else[rw]
         super(cap, lim);
         this.isReadOnly = true;
 #end[rw]
-#if[byte]
-        this.address = arrayBaseOffset;
-#end[byte]
     }
 
     Heap$Type$Buffer$RW$($type$[] buf, int off, int len) { // package-private
@@ -86,13 +77,11 @@
         hb = buf;
         offset = 0;
         */
+        this.address = ARRAY_BASE_OFFSET;
 #else[rw]
         super(buf, off, len);
         this.isReadOnly = true;
 #end[rw]
-#if[byte]
-        this.address = arrayBaseOffset;
-#end[byte]
     }
 
     protected Heap$Type$Buffer$RW$($type$[] buf,
@@ -105,13 +94,11 @@
         hb = buf;
         offset = off;
         */
+        this.address = ARRAY_BASE_OFFSET + off * ARRAY_INDEX_SCALE;
 #else[rw]
         super(buf, mark, pos, lim, cap, off);
         this.isReadOnly = true;
 #end[rw]
-#if[byte]
-        this.address = arrayBaseOffset + off;
-#end[byte]
     }
 
     public $Type$Buffer slice() {
@@ -296,18 +283,18 @@
 #if[rw]
 
     public char getChar() {
-        return unsafe.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
+        return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
     }
 
     public char getChar(int i) {
-        return unsafe.getCharUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
+        return UNSAFE.getCharUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
     }
 
 #end[rw]
 
     public $Type$Buffer putChar(char x) {
 #if[rw]
-        unsafe.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
+        UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -316,7 +303,7 @@
 
     public $Type$Buffer putChar(int i, char x) {
 #if[rw]
-        unsafe.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
+        UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -347,18 +334,18 @@
 #if[rw]
 
     public short getShort() {
-        return unsafe.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
+        return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
     }
 
     public short getShort(int i) {
-        return unsafe.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
+        return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
     }
 
 #end[rw]
 
     public $Type$Buffer putShort(short x) {
 #if[rw]
-        unsafe.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
+        UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -367,7 +354,7 @@
 
     public $Type$Buffer putShort(int i, short x) {
 #if[rw]
-        unsafe.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
+        UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -398,18 +385,18 @@
 #if[rw]
 
     public int getInt() {
-        return unsafe.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
+        return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
     }
 
     public int getInt(int i) {
-        return unsafe.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
+        return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
     }
 
 #end[rw]
 
     public $Type$Buffer putInt(int x) {
 #if[rw]
-        unsafe.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
+        UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -418,7 +405,7 @@
 
     public $Type$Buffer putInt(int i, int x) {
 #if[rw]
-        unsafe.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
+        UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -449,18 +436,18 @@
 #if[rw]
 
     public long getLong() {
-        return unsafe.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
+        return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
     }
 
     public long getLong(int i) {
-        return unsafe.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
+        return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
     }
 
 #end[rw]
 
     public $Type$Buffer putLong(long x) {
 #if[rw]
-        unsafe.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
+        UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -469,7 +456,7 @@
 
     public $Type$Buffer putLong(int i, long x) {
 #if[rw]
-        unsafe.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
+        UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -500,12 +487,12 @@
 #if[rw]
 
     public float getFloat() {
-        int x = unsafe.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
+        int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
         return Float.intBitsToFloat(x);
     }
 
     public float getFloat(int i) {
-        int x = unsafe.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
+        int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
         return Float.intBitsToFloat(x);
     }
 
@@ -514,7 +501,7 @@
     public $Type$Buffer putFloat(float x) {
 #if[rw]
         int y = Float.floatToRawIntBits(x);
-        unsafe.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
+        UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -524,7 +511,7 @@
     public $Type$Buffer putFloat(int i, float x) {
 #if[rw]
         int y = Float.floatToRawIntBits(x);
-        unsafe.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
+        UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -555,12 +542,12 @@
 #if[rw]
 
     public double getDouble() {
-        long x = unsafe.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
+        long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
         return Double.longBitsToDouble(x);
     }
 
     public double getDouble(int i) {
-        long x = unsafe.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
+        long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
         return Double.longBitsToDouble(x);
     }
 
@@ -569,7 +556,7 @@
     public $Type$Buffer putDouble(double x) {
 #if[rw]
         long y = Double.doubleToRawLongBits(x);
-        unsafe.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
+        UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -579,7 +566,7 @@
     public $Type$Buffer putDouble(int i, double x) {
 #if[rw]
         long y = Double.doubleToRawLongBits(x);
-        unsafe.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
+        UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();
@@ -643,7 +630,11 @@
     public ByteOrder order() {
         return ByteOrder.nativeOrder();
     }
+#end[!byte]
+#if[char]
 
-#end[!byte]
-
+    ByteOrder charRegionOrder() {
+        return order();
+    }
+#end[char]
 }
--- a/src/java.base/share/classes/java/nio/StringCharBuffer.java	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/StringCharBuffer.java	Wed Dec 20 09:14:52 2017 -0800
@@ -127,4 +127,30 @@
         return ByteOrder.nativeOrder();
     }
 
+    ByteOrder charRegionOrder() {
+        return null;
+    }
+
+    public boolean equals(Object ob) {
+        if (this == ob)
+            return true;
+        if (!(ob instanceof CharBuffer))
+            return false;
+        CharBuffer that = (CharBuffer)ob;
+        if (this.remaining() != that.remaining())
+            return false;
+        return BufferMismatch.mismatch(this, this.position(),
+                                       that, that.position(),
+                                       this.remaining()) < 0;
+    }
+
+    public int compareTo(CharBuffer that) {
+        int i = BufferMismatch.mismatch(this, this.position(),
+                                        that, that.position(),
+                                        Math.min(this.remaining(), that.remaining()));
+        if (i >= 0) {
+            return Character.compare(this.get(this.position() + i), that.get(this.position() + i));
+        }
+        return this.remaining() - that.remaining();
+    }
 }
--- a/src/java.base/share/classes/java/nio/X-Buffer.java.template	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template	Wed Dec 20 09:14:52 2017 -0800
@@ -36,6 +36,8 @@
 import java.util.stream.$Streamtype$Stream;
 #end[streamableType]
 
+import jdk.internal.util.ArraysSupport;
+
 /**
  * $A$ $type$ buffer.
  *
@@ -287,6 +289,11 @@
         this(mark, pos, lim, cap, null, 0);
     }
 
+    @Override
+    Object base() {
+        return hb;
+    }
+
 #if[byte]
 
     /**
@@ -1297,19 +1304,9 @@
         $Type$Buffer that = ($Type$Buffer)ob;
         if (this.remaining() != that.remaining())
             return false;
-        int p = this.position();
-        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
-            if (!equals(this.get(i), that.get(j)))
-                return false;
-        return true;
-    }
-
-    private static boolean equals($type$ x, $type$ y) {
-#if[floatingPointType]
-        return (x == y) || ($Fulltype$.isNaN(x) && $Fulltype$.isNaN(y));
-#else[floatingPointType]
-        return x == y;
-#end[floatingPointType]
+        return BufferMismatch.mismatch(this, this.position(),
+                                       that, that.position(),
+                                       this.remaining()) < 0;
     }
 
     /**
@@ -1336,11 +1333,11 @@
      *          is less than, equal to, or greater than the given buffer
      */
     public int compareTo($Type$Buffer that) {
-        int n = this.position() + Math.min(this.remaining(), that.remaining());
-        for (int i = this.position(), j = that.position(); i < n; i++, j++) {
-            int cmp = compare(this.get(i), that.get(j));
-            if (cmp != 0)
-                return cmp;
+        int i = BufferMismatch.mismatch(this, this.position(),
+                                        that, that.position(),
+                                        Math.min(this.remaining(), that.remaining()));
+        if (i >= 0) {
+            return compare(this.get(this.position() + i), that.get(this.position() + i));
         }
         return this.remaining() - that.remaining();
     }
@@ -1571,6 +1568,12 @@
 
 #end[!byte]
 
+#if[char]
+    // The order or null if the buffer does not cover a memory region,
+    // such as StringCharBuffer
+    abstract ByteOrder charRegionOrder();
+#end[char]
+
 #if[byte]
 
     boolean bigEndian                                   // package-private
--- a/src/java.base/share/classes/java/util/Arrays.java	Wed Dec 20 09:14:06 2017 -0800
+++ b/src/java.base/share/classes/java/util/Arrays.java	Wed Dec 20 09:14:52 2017 -0800
@@ -26,6 +26,7 @@
 package java.util;
 
 import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.util.ArraysSupport;
 
 import java.lang.reflect.Array;
 import java.util.concurrent.ForkJoinPool;
--- a/src/java.base/share/classes/java/util/ArraysSupport.java	Wed Dec 20 09:14:06 2017 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,545 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util;
-
-import jdk.internal.HotSpotIntrinsicCandidate;
-import jdk.internal.misc.Unsafe;
-
-/**
- * Utility methods to find a mismatch between two primitive arrays.
- *
- * <p>Array equality and lexicographical comparison can be built on top of
- * array mismatch functionality.
- *
- * <p>The mismatch method implementation, {@link #vectorizedMismatch}, leverages
- * vector-based techniques to access and compare the contents of two arrays.
- * The Java implementation uses {@code Unsafe.getLongUnaligned} to access the
- * content of an array, thus access is supported on platforms that do not
- * support unaligned access.  For a byte[] array, 8 bytes (64 bits) can be
- * accessed and compared as a unit rather than individually, which increases
- * the performance when the method is compiled by the HotSpot VM.  On supported
- * platforms the mismatch implementation is intrinsified to leverage SIMD
- * instructions.  So for a byte[] array, 16 bytes (128 bits), 32 bytes
- * (256 bits), and perhaps in the future even 64 bytes (512 bits), platform
- * permitting, can be accessed and compared as a unit, which further increases
- * the performance over the Java implementation.
- *
- * <p>None of the mismatch methods perform array bounds checks.  It is the
- * responsibility of the caller (direct or otherwise) to perform such checks
- * before calling this method.
- */
-class ArraysSupport {
-    static final Unsafe U = Unsafe.getUnsafe();
-
-    private static final boolean BIG_ENDIAN = U.isBigEndian();
-
-    private static final int LOG2_ARRAY_BOOLEAN_INDEX_SCALE = exactLog2(Unsafe.ARRAY_BOOLEAN_INDEX_SCALE);
-    private static final int LOG2_ARRAY_BYTE_INDEX_SCALE = exactLog2(Unsafe.ARRAY_BYTE_INDEX_SCALE);
-    private static final int LOG2_ARRAY_CHAR_INDEX_SCALE = exactLog2(Unsafe.ARRAY_CHAR_INDEX_SCALE);
-    private static final int LOG2_ARRAY_SHORT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_SHORT_INDEX_SCALE);
-    private static final int LOG2_ARRAY_INT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_INT_INDEX_SCALE);
-    private static final int LOG2_ARRAY_LONG_INDEX_SCALE = exactLog2(Unsafe.ARRAY_LONG_INDEX_SCALE);
-    private static final int LOG2_ARRAY_FLOAT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_FLOAT_INDEX_SCALE);
-    private static final int LOG2_ARRAY_DOUBLE_INDEX_SCALE = exactLog2(Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
-
-    private static final int LOG2_BYTE_BIT_SIZE = exactLog2(Byte.SIZE);
-
-    private static int exactLog2(int scale) {
-        if ((scale & (scale - 1)) != 0)
-            throw new Error("data type scale not a power of two");
-        return Integer.numberOfTrailingZeros(scale);
-    }
-
-    private ArraysSupport() {}
-
-    /**
-     * Find the relative index of the first mismatching pair of elements in two
-     * primitive arrays of the same component type.  Pairs of elements will be
-     * tested in order relative to given offsets into both arrays.
-     *
-     * <p>This method does not perform type checks or bounds checks.  It is the
-     * responsibility of the caller to perform such checks before calling this
-     * method.
-     *
-     * <p>The given offsets, in bytes, need not be aligned according to the
-     * given log<sub>2</sub> size the array elements.  More specifically, an
-     * offset modulus the size need not be zero.
-     *
-     * @param a the first array to be tested for mismatch, or {@code null} for
-     * direct memory access
-     * @param aOffset the relative offset, in bytes, from the base address of
-     * the first array to test from, otherwise if the first array is
-     * {@code null}, an absolute address pointing to the first element to test.
-     * @param b the second array to be tested for mismatch, or {@code null} for
-     * direct memory access
-     * @param bOffset the relative offset, in bytes, from the base address of
-     * the second array to test from, otherwise if the second array is
-     * {@code null}, an absolute address pointing to the first element to test.
-     * @param length the number of array elements to test
-     * @param log2ArrayIndexScale log<sub>2</sub> of the array index scale, that
-     * corresponds to the size, in bytes, of an array element.
-     * @return if a mismatch is found a relative index, between 0 (inclusive)
-     * and {@code length} (exclusive), of the first mismatching pair of elements
-     * in the two arrays.  Otherwise, if a mismatch is not found the bitwise
-     * compliment of the number of remaining pairs of elements to be checked in
-     * the tail of the two arrays.
-     */
-    @HotSpotIntrinsicCandidate
-    static int vectorizedMismatch(Object a, long aOffset,
-                                  Object b, long bOffset,
-                                  int length,
-                                  int log2ArrayIndexScale) {
-        // assert a.getClass().isArray();
-        // assert b.getClass().isArray();
-        // assert 0 <= length <= sizeOf(a)
-        // assert 0 <= length <= sizeOf(b)
-        // assert 0 <= log2ArrayIndexScale <= 3
-
-        int log2ValuesPerWidth = LOG2_ARRAY_LONG_INDEX_SCALE - log2ArrayIndexScale;
-        int wi = 0;
-        for (; wi < length >> log2ValuesPerWidth; wi++) {
-            long bi = ((long) wi) << LOG2_ARRAY_LONG_INDEX_SCALE;
-            long av = U.getLongUnaligned(a, aOffset + bi);
-            long bv = U.getLongUnaligned(b, bOffset + bi);
-            if (av != bv) {
-                long x = av ^ bv;
-                int o = BIG_ENDIAN
-                        ? Long.numberOfLeadingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale)
-                        : Long.numberOfTrailingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale);
-                return (wi << log2ValuesPerWidth) + o;
-            }
-        }
-
-        // Calculate the tail of remaining elements to check
-        int tail = length - (wi << log2ValuesPerWidth);
-
-        if (log2ArrayIndexScale < LOG2_ARRAY_INT_INDEX_SCALE) {
-            int wordTail = 1 << (LOG2_ARRAY_INT_INDEX_SCALE - log2ArrayIndexScale);
-            // Handle 4 bytes or 2 chars in the tail using int width
-            if (tail >= wordTail) {
-                long bi = ((long) wi) << LOG2_ARRAY_LONG_INDEX_SCALE;
-                int av = U.getIntUnaligned(a, aOffset + bi);
-                int bv = U.getIntUnaligned(b, bOffset + bi);
-                if (av != bv) {
-                    int x = av ^ bv;
-                    int o = BIG_ENDIAN
-                            ? Integer.numberOfLeadingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale)
-                            : Integer.numberOfTrailingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale);
-                    return (wi << log2ValuesPerWidth) + o;
-                }
-                tail -= wordTail;
-            }
-            return ~tail;
-        }
-        else {
-            return ~tail;
-        }
-    }
-
-    // Booleans
-    // Each boolean element takes up one byte
-
-    static int mismatch(boolean[] a,
-                        boolean[] b,
-                        int length) {
-        int i = 0;
-        if (length > 7) {
-            i = vectorizedMismatch(
-                    a, Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
-                    b, Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
-                    length, LOG2_ARRAY_BOOLEAN_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[i] != b[i])
-                return i;
-        }
-        return -1;
-    }
-
-    static int mismatch(boolean[] a, int aFromIndex,
-                        boolean[] b, int bFromIndex,
-                        int length) {
-        int i = 0;
-        if (length > 7) {
-            int aOffset = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + aFromIndex;
-            int bOffset = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + bFromIndex;
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_BOOLEAN_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[aFromIndex + i] != b[bFromIndex + i])
-                return i;
-        }
-        return -1;
-    }
-
-
-    // Bytes
-
-    /**
-     * Find the index of a mismatch between two arrays.
-     *
-     * <p>This method does not perform bounds checks. It is the responsibility
-     * of the caller to perform such bounds checks before calling this method.
-     *
-     * @param a the first array to be tested for a mismatch
-     * @param b the second array to be tested for a mismatch
-     * @param length the number of bytes from each array to check
-     * @return the index of a mismatch between the two arrays, otherwise -1 if
-     * no mismatch.  The index will be within the range of (inclusive) 0 to
-     * (exclusive) the smaller of the two array lengths.
-     */
-    static int mismatch(byte[] a,
-                        byte[] b,
-                        int length) {
-        // ISSUE: defer to index receiving methods if performance is good
-        // assert length <= a.length
-        // assert length <= b.length
-
-        int i = 0;
-        if (length > 7) {
-            i = vectorizedMismatch(
-                    a, Unsafe.ARRAY_BYTE_BASE_OFFSET,
-                    b, Unsafe.ARRAY_BYTE_BASE_OFFSET,
-                    length, LOG2_ARRAY_BYTE_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            // Align to tail
-            i = length - ~i;
-//            assert i >= 0 && i <= 7;
-        }
-        // Tail < 8 bytes
-        for (; i < length; i++) {
-            if (a[i] != b[i])
-                return i;
-        }
-        return -1;
-    }
-
-    /**
-     * Find the relative index of a mismatch between two arrays starting from
-     * given indexes.
-     *
-     * <p>This method does not perform bounds checks. It is the responsibility
-     * of the caller to perform such bounds checks before calling this method.
-     *
-     * @param a the first array to be tested for a mismatch
-     * @param aFromIndex the index of the first element (inclusive) in the first
-     * array to be compared
-     * @param b the second array to be tested for a mismatch
-     * @param bFromIndex the index of the first element (inclusive) in the
-     * second array to be compared
-     * @param length the number of bytes from each array to check
-     * @return the relative index of a mismatch between the two arrays,
-     * otherwise -1 if no mismatch.  The index will be within the range of
-     * (inclusive) 0 to (exclusive) the smaller of the two array bounds.
-     */
-    static int mismatch(byte[] a, int aFromIndex,
-                        byte[] b, int bFromIndex,
-                        int length) {
-        // assert 0 <= aFromIndex < a.length
-        // assert 0 <= aFromIndex + length <= a.length
-        // assert 0 <= bFromIndex < b.length
-        // assert 0 <= bFromIndex + length <= b.length
-        // assert length >= 0
-
-        int i = 0;
-        if (length > 7) {
-            int aOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + aFromIndex;
-            int bOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + bFromIndex;
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_BYTE_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[aFromIndex + i] != b[bFromIndex + i])
-                return i;
-        }
-        return -1;
-    }
-
-
-    // Chars
-
-    static int mismatch(char[] a,
-                        char[] b,
-                        int length) {
-        int i = 0;
-        if (length > 3) {
-            i = vectorizedMismatch(
-                    a, Unsafe.ARRAY_CHAR_BASE_OFFSET,
-                    b, Unsafe.ARRAY_CHAR_BASE_OFFSET,
-                    length, LOG2_ARRAY_CHAR_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[i] != b[i])
-                return i;
-        }
-        return -1;
-    }
-
-    static int mismatch(char[] a, int aFromIndex,
-                        char[] b, int bFromIndex,
-                        int length) {
-        int i = 0;
-        if (length > 3) {
-            int aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE);
-            int bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE);
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_CHAR_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[aFromIndex + i] != b[bFromIndex + i])
-                return i;
-        }
-        return -1;
-    }
-
-
-    // Shorts
-
-    static int mismatch(short[] a,
-                        short[] b,
-                        int length) {
-        int i = 0;
-        if (length > 3) {
-            i = vectorizedMismatch(
-                    a, Unsafe.ARRAY_SHORT_BASE_OFFSET,
-                    b, Unsafe.ARRAY_SHORT_BASE_OFFSET,
-                    length, LOG2_ARRAY_SHORT_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[i] != b[i])
-                return i;
-        }
-        return -1;
-    }
-
-    static int mismatch(short[] a, int aFromIndex,
-                        short[] b, int bFromIndex,
-                        int length) {
-        int i = 0;
-        if (length > 3) {
-            int aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE);
-            int bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE);
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_SHORT_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[aFromIndex + i] != b[bFromIndex + i])
-                return i;
-        }
-        return -1;
-    }
-
-
-    // Ints
-
-    static int mismatch(int[] a,
-                        int[] b,
-                        int length) {
-        int i = 0;
-        if (length > 1) {
-            i = vectorizedMismatch(
-                    a, Unsafe.ARRAY_INT_BASE_OFFSET,
-                    b, Unsafe.ARRAY_INT_BASE_OFFSET,
-                    length, LOG2_ARRAY_INT_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[i] != b[i])
-                return i;
-        }
-        return -1;
-    }
-
-    static int mismatch(int[] a, int aFromIndex,
-                        int[] b, int bFromIndex,
-                        int length) {
-        int i = 0;
-        if (length > 1) {
-            int aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE);
-            int bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE);
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_INT_INDEX_SCALE);
-            if (i >= 0)
-                return i;
-            i = length - ~i;
-        }
-        for (; i < length; i++) {
-            if (a[aFromIndex + i] != b[bFromIndex + i])
-                return i;
-        }
-        return -1;
-    }
-
-
-    // Floats
-
-    static int mismatch(float[] a,
-                        float[] b,
-                        int length) {
-        return mismatch(a, 0, b, 0, length);
-    }
-
-    static int mismatch(float[] a, int aFromIndex,
-                        float[] b, int bFromIndex,
-                        int length) {
-        int i = 0;
-        if (length > 1) {
-            int aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE);
-            int bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE);
-            i = vectorizedMismatch(
-                    a, aOffset,
-                    b, bOffset,
-                    length, LOG2_ARRAY_FLOAT_INDEX_SCALE);
-            // Mismatched
-            if (i >= 0) {
-                // Check if mismatch is not associated with two NaN values
-                if (!Float.isNaN(a[aFromIndex + i]) || !Float.isNaN(b[bFromIndex + i]))
-                    return i;
-
-                // Mismatch on two different NaN values that are normalized to match
-                // Fall back to slow mechanism
-                // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
-                // However, requires that returned value be relative to input ranges
-                i++;
-            }
-            // Matched
-            else {
-                i = length - ~i;
-            }
-        }
-        for (; i < length; i++) {
-            if (Float.floatToIntBits(a[aFromIndex + i]) != Float.floatToIntBits(b[bFromIndex + i]))
-                return i;
-        }
-        return -1;
-    }
-
-    // 64 bit sizes
-
-    // Long
-
-    static int mismatch(long[] a,
-                        long[] b,
-                        int length) {
-        if (length == 0) {
-            return -1;
-        }
-        int i = vectorizedMismatch(
-                a, Unsafe.ARRAY_LONG_BASE_OFFSET,
-                b, Unsafe.ARRAY_LONG_BASE_OFFSET,
-                length, LOG2_ARRAY_LONG_INDEX_SCALE);
-        return i >= 0 ? i : -1;
-    }
-
-    static int mismatch(long[] a, int aFromIndex,
-                        long[] b, int bFromIndex,
-                        int length) {
-        if (length == 0) {
-            return -1;
-        }
-        int aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE);
-        int bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE);
-        int i = vectorizedMismatch(
-                a, aOffset,
-                b, bOffset,
-                length, LOG2_ARRAY_LONG_INDEX_SCALE);
-        return i >= 0 ? i : -1;
-    }
-
-
-    // Double
-
-    static int mismatch(double[] a,
-                        double[] b,
-                        int length) {
-        return mismatch(a, 0, b, 0, length);
-    }
-
-    static int mismatch(double[] a, int aFromIndex,
-                        double[] b, int bFromIndex,
-                        int length) {
-        if (length == 0) {
-            return -1;
-        }
-        int aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE);
-        int bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE);
-        int i = vectorizedMismatch(
-                a, aOffset,
-                b, bOffset,
-                length, LOG2_ARRAY_DOUBLE_INDEX_SCALE);
-        if (i >= 0) {
-            // Check if mismatch is not associated with two NaN values
-            if (!Double.isNaN(a[aFromIndex + i]) || !Double.isNaN(b[bFromIndex + i]))
-                return i;
-
-            // Mismatch on two different NaN values that are normalized to match
-            // Fall back to slow mechanism
-            // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
-            // However, requires that returned value be relative to input ranges
-            i++;
-            for (; i < length; i++) {
-                if (Double.doubleToLongBits(a[aFromIndex + i]) != Double.doubleToLongBits(b[bFromIndex + i]))
-                    return i;
-            }
-        }
-
-        return -1;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java	Wed Dec 20 09:14:52 2017 -0800
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.util;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.misc.Unsafe;
+
+/**
+ * Utility methods to find a mismatch between two primitive arrays.
+ *
+ * <p>Array equality and lexicographical comparison can be built on top of
+ * array mismatch functionality.
+ *
+ * <p>The mismatch method implementation, {@link #vectorizedMismatch}, leverages
+ * vector-based techniques to access and compare the contents of two arrays.
+ * The Java implementation uses {@code Unsafe.getLongUnaligned} to access the
+ * content of an array, thus access is supported on platforms that do not
+ * support unaligned access.  For a byte[] array, 8 bytes (64 bits) can be
+ * accessed and compared as a unit rather than individually, which increases
+ * the performance when the method is compiled by the HotSpot VM.  On supported
+ * platforms the mismatch implementation is intrinsified to leverage SIMD
+ * instructions.  So for a byte[] array, 16 bytes (128 bits), 32 bytes
+ * (256 bits), and perhaps in the future even 64 bytes (512 bits), platform
+ * permitting, can be accessed and compared as a unit, which further increases
+ * the performance over the Java implementation.
+ *
+ * <p>None of the mismatch methods perform array bounds checks.  It is the
+ * responsibility of the caller (direct or otherwise) to perform such checks
+ * before calling this method.
+ */
+public class ArraysSupport {
+    static final Unsafe U = Unsafe.getUnsafe();
+
+    private static final boolean BIG_ENDIAN = U.isBigEndian();
+
+    public static final int LOG2_ARRAY_BOOLEAN_INDEX_SCALE = exactLog2(Unsafe.ARRAY_BOOLEAN_INDEX_SCALE);
+    public static final int LOG2_ARRAY_BYTE_INDEX_SCALE = exactLog2(Unsafe.ARRAY_BYTE_INDEX_SCALE);
+    public static final int LOG2_ARRAY_CHAR_INDEX_SCALE = exactLog2(Unsafe.ARRAY_CHAR_INDEX_SCALE);
+    public static final int LOG2_ARRAY_SHORT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_SHORT_INDEX_SCALE);
+    public static final int LOG2_ARRAY_INT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_INT_INDEX_SCALE);
+    public static final int LOG2_ARRAY_LONG_INDEX_SCALE = exactLog2(Unsafe.ARRAY_LONG_INDEX_SCALE);
+    public static final int LOG2_ARRAY_FLOAT_INDEX_SCALE = exactLog2(Unsafe.ARRAY_FLOAT_INDEX_SCALE);
+    public static final int LOG2_ARRAY_DOUBLE_INDEX_SCALE = exactLog2(Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+
+    private static final int LOG2_BYTE_BIT_SIZE = exactLog2(Byte.SIZE);
+
+    private static int exactLog2(int scale) {
+        if ((scale & (scale - 1)) != 0)
+            throw new Error("data type scale not a power of two");
+        return Integer.numberOfTrailingZeros(scale);
+    }
+
+    private ArraysSupport() {}
+
+    /**
+     * Find the relative index of the first mismatching pair of elements in two
+     * primitive arrays of the same component type.  Pairs of elements will be
+     * tested in order relative to given offsets into both arrays.
+     *
+     * <p>This method does not perform type checks or bounds checks.  It is the
+     * responsibility of the caller to perform such checks before calling this
+     * method.
+     *
+     * <p>The given offsets, in bytes, need not be aligned according to the
+     * given log<sub>2</sub> size the array elements.  More specifically, an
+     * offset modulus the size need not be zero.
+     *
+     * @param a the first array to be tested for mismatch, or {@code null} for
+     * direct memory access
+     * @param aOffset the relative offset, in bytes, from the base address of
+     * the first array to test from, otherwise if the first array is
+     * {@code null}, an absolute address pointing to the first element to test.
+     * @param b the second array to be tested for mismatch, or {@code null} for
+     * direct memory access
+     * @param bOffset the relative offset, in bytes, from the base address of
+     * the second array to test from, otherwise if the second array is
+     * {@code null}, an absolute address pointing to the first element to test.
+     * @param length the number of array elements to test
+     * @param log2ArrayIndexScale log<sub>2</sub> of the array index scale, that
+     * corresponds to the size, in bytes, of an array element.
+     * @return if a mismatch is found a relative index, between 0 (inclusive)
+     * and {@code length} (exclusive), of the first mismatching pair of elements
+     * in the two arrays.  Otherwise, if a mismatch is not found the bitwise
+     * compliment of the number of remaining pairs of elements to be checked in
+     * the tail of the two arrays.
+     */
+    @HotSpotIntrinsicCandidate
+    public static int vectorizedMismatch(Object a, long aOffset,
+                                         Object b, long bOffset,
+                                         int length,
+                                         int log2ArrayIndexScale) {
+        // assert a.getClass().isArray();
+        // assert b.getClass().isArray();
+        // assert 0 <= length <= sizeOf(a)
+        // assert 0 <= length <= sizeOf(b)
+        // assert 0 <= log2ArrayIndexScale <= 3
+
+        int log2ValuesPerWidth = LOG2_ARRAY_LONG_INDEX_SCALE - log2ArrayIndexScale;
+        int wi = 0;
+        for (; wi < length >> log2ValuesPerWidth; wi++) {
+            long bi = ((long) wi) << LOG2_ARRAY_LONG_INDEX_SCALE;
+            long av = U.getLongUnaligned(a, aOffset + bi);
+            long bv = U.getLongUnaligned(b, bOffset + bi);
+            if (av != bv) {
+                long x = av ^ bv;
+                int o = BIG_ENDIAN
+                        ? Long.numberOfLeadingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale)
+                        : Long.numberOfTrailingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale);
+                return (wi << log2ValuesPerWidth) + o;
+            }
+        }
+
+        // Calculate the tail of remaining elements to check
+        int tail = length - (wi << log2ValuesPerWidth);
+
+        if (log2ArrayIndexScale < LOG2_ARRAY_INT_INDEX_SCALE) {
+            int wordTail = 1 << (LOG2_ARRAY_INT_INDEX_SCALE - log2ArrayIndexScale);
+            // Handle 4 bytes or 2 chars in the tail using int width
+            if (tail >= wordTail) {
+                long bi = ((long) wi) << LOG2_ARRAY_LONG_INDEX_SCALE;
+                int av = U.getIntUnaligned(a, aOffset + bi);
+                int bv = U.getIntUnaligned(b, bOffset + bi);
+                if (av != bv) {
+                    int x = av ^ bv;
+                    int o = BIG_ENDIAN
+                            ? Integer.numberOfLeadingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale)
+                            : Integer.numberOfTrailingZeros(x) >> (LOG2_BYTE_BIT_SIZE + log2ArrayIndexScale);
+                    return (wi << log2ValuesPerWidth) + o;
+                }
+                tail -= wordTail;
+            }
+            return ~tail;
+        }
+        else {
+            return ~tail;
+        }
+    }
+
+    // Booleans
+    // Each boolean element takes up one byte
+
+    public static int mismatch(boolean[] a,
+                               boolean[] b,
+                               int length) {
+        int i = 0;
+        if (length > 7) {
+            i = vectorizedMismatch(
+                    a, Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
+                    b, Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
+                    length, LOG2_ARRAY_BOOLEAN_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[i] != b[i])
+                return i;
+        }
+        return -1;
+    }
+
+    public static int mismatch(boolean[] a, int aFromIndex,
+                               boolean[] b, int bFromIndex,
+                               int length) {
+        int i = 0;
+        if (length > 7) {
+            int aOffset = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + aFromIndex;
+            int bOffset = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + bFromIndex;
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_BOOLEAN_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[aFromIndex + i] != b[bFromIndex + i])
+                return i;
+        }
+        return -1;
+    }
+
+
+    // Bytes
+
+    /**
+     * Find the index of a mismatch between two arrays.
+     *
+     * <p>This method does not perform bounds checks. It is the responsibility
+     * of the caller to perform such bounds checks before calling this method.
+     *
+     * @param a the first array to be tested for a mismatch
+     * @param b the second array to be tested for a mismatch
+     * @param length the number of bytes from each array to check
+     * @return the index of a mismatch between the two arrays, otherwise -1 if
+     * no mismatch.  The index will be within the range of (inclusive) 0 to
+     * (exclusive) the smaller of the two array lengths.
+     */
+    public static int mismatch(byte[] a,
+                               byte[] b,
+                               int length) {
+        // ISSUE: defer to index receiving methods if performance is good
+        // assert length <= a.length
+        // assert length <= b.length
+
+        int i = 0;
+        if (length > 7) {
+            i = vectorizedMismatch(
+                    a, Unsafe.ARRAY_BYTE_BASE_OFFSET,
+                    b, Unsafe.ARRAY_BYTE_BASE_OFFSET,
+                    length, LOG2_ARRAY_BYTE_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            // Align to tail
+            i = length - ~i;
+//            assert i >= 0 && i <= 7;
+        }
+        // Tail < 8 bytes
+        for (; i < length; i++) {
+            if (a[i] != b[i])
+                return i;
+        }
+        return -1;
+    }
+
+    /**
+     * Find the relative index of a mismatch between two arrays starting from
+     * given indexes.
+     *
+     * <p>This method does not perform bounds checks. It is the responsibility
+     * of the caller to perform such bounds checks before calling this method.
+     *
+     * @param a the first array to be tested for a mismatch
+     * @param aFromIndex the index of the first element (inclusive) in the first
+     * array to be compared
+     * @param b the second array to be tested for a mismatch
+     * @param bFromIndex the index of the first element (inclusive) in the
+     * second array to be compared
+     * @param length the number of bytes from each array to check
+     * @return the relative index of a mismatch between the two arrays,
+     * otherwise -1 if no mismatch.  The index will be within the range of
+     * (inclusive) 0 to (exclusive) the smaller of the two array bounds.
+     */
+    public static int mismatch(byte[] a, int aFromIndex,
+                               byte[] b, int bFromIndex,
+                               int length) {
+        // assert 0 <= aFromIndex < a.length
+        // assert 0 <= aFromIndex + length <= a.length
+        // assert 0 <= bFromIndex < b.length
+        // assert 0 <= bFromIndex + length <= b.length
+        // assert length >= 0
+
+        int i = 0;
+        if (length > 7) {
+            int aOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + aFromIndex;
+            int bOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + bFromIndex;
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_BYTE_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[aFromIndex + i] != b[bFromIndex + i])
+                return i;
+        }
+        return -1;
+    }
+
+
+    // Chars
+
+    public static int mismatch(char[] a,
+                               char[] b,
+                               int length) {
+        int i = 0;
+        if (length > 3) {
+            i = vectorizedMismatch(
+                    a, Unsafe.ARRAY_CHAR_BASE_OFFSET,
+                    b, Unsafe.ARRAY_CHAR_BASE_OFFSET,
+                    length, LOG2_ARRAY_CHAR_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[i] != b[i])
+                return i;
+        }
+        return -1;
+    }
+
+    public static int mismatch(char[] a, int aFromIndex,
+                               char[] b, int bFromIndex,
+                               int length) {
+        int i = 0;
+        if (length > 3) {
+            int aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE);
+            int bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE);
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_CHAR_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[aFromIndex + i] != b[bFromIndex + i])
+                return i;
+        }
+        return -1;
+    }
+
+
+    // Shorts
+
+    public static int mismatch(short[] a,
+                               short[] b,
+                               int length) {
+        int i = 0;
+        if (length > 3) {
+            i = vectorizedMismatch(
+                    a, Unsafe.ARRAY_SHORT_BASE_OFFSET,
+                    b, Unsafe.ARRAY_SHORT_BASE_OFFSET,
+                    length, LOG2_ARRAY_SHORT_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[i] != b[i])
+                return i;
+        }
+        return -1;
+    }
+
+    public static int mismatch(short[] a, int aFromIndex,
+                               short[] b, int bFromIndex,
+                               int length) {
+        int i = 0;
+        if (length > 3) {
+            int aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE);
+            int bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE);
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_SHORT_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[aFromIndex + i] != b[bFromIndex + i])
+                return i;
+        }
+        return -1;
+    }
+
+
+    // Ints
+
+    public static int mismatch(int[] a,
+                               int[] b,
+                               int length) {
+        int i = 0;
+        if (length > 1) {
+            i = vectorizedMismatch(
+                    a, Unsafe.ARRAY_INT_BASE_OFFSET,
+                    b, Unsafe.ARRAY_INT_BASE_OFFSET,
+                    length, LOG2_ARRAY_INT_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[i] != b[i])
+                return i;
+        }
+        return -1;
+    }
+
+    public static int mismatch(int[] a, int aFromIndex,
+                               int[] b, int bFromIndex,
+                               int length) {
+        int i = 0;
+        if (length > 1) {
+            int aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE);
+            int bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE);
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_INT_INDEX_SCALE);
+            if (i >= 0)
+                return i;
+            i = length - ~i;
+        }
+        for (; i < length; i++) {
+            if (a[aFromIndex + i] != b[bFromIndex + i])
+                return i;
+        }
+        return -1;
+    }
+
+
+    // Floats
+
+    public static int mismatch(float[] a,
+                               float[] b,
+                               int length) {
+        return mismatch(a, 0, b, 0, length);
+    }
+
+    public static int mismatch(float[] a, int aFromIndex,
+                               float[] b, int bFromIndex,
+                               int length) {
+        int i = 0;
+        if (length > 1) {
+            int aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE);
+            int bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE);
+            i = vectorizedMismatch(
+                    a, aOffset,
+                    b, bOffset,
+                    length, LOG2_ARRAY_FLOAT_INDEX_SCALE);
+            // Mismatched
+            if (i >= 0) {
+                // Check if mismatch is not associated with two NaN values
+                if (!Float.isNaN(a[aFromIndex + i]) || !Float.isNaN(b[bFromIndex + i]))
+                    return i;
+
+                // Mismatch on two different NaN values that are normalized to match
+                // Fall back to slow mechanism
+                // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
+                // However, requires that returned value be relative to input ranges
+                i++;
+            }
+            // Matched
+            else {
+                i = length - ~i;
+            }
+        }
+        for (; i < length; i++) {
+            if (Float.floatToIntBits(a[aFromIndex + i]) != Float.floatToIntBits(b[bFromIndex + i]))
+                return i;
+        }
+        return -1;
+    }
+
+    // 64 bit sizes
+
+    // Long
+
+    public static int mismatch(long[] a,
+                               long[] b,
+                               int length) {
+        if (length == 0) {
+            return -1;
+        }
+        int i = vectorizedMismatch(
+                a, Unsafe.ARRAY_LONG_BASE_OFFSET,
+                b, Unsafe.ARRAY_LONG_BASE_OFFSET,
+                length, LOG2_ARRAY_LONG_INDEX_SCALE);
+        return i >= 0 ? i : -1;
+    }
+
+    public static int mismatch(long[] a, int aFromIndex,
+                               long[] b, int bFromIndex,
+                               int length) {
+        if (length == 0) {
+            return -1;
+        }
+        int aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE);
+        int bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE);
+        int i = vectorizedMismatch(
+                a, aOffset,
+                b, bOffset,
+                length, LOG2_ARRAY_LONG_INDEX_SCALE);
+        return i >= 0 ? i : -1;
+    }
+
+
+    // Double
+
+    public static int mismatch(double[] a,
+                               double[] b,
+                               int length) {
+        return mismatch(a, 0, b, 0, length);
+    }
+
+    public static int mismatch(double[] a, int aFromIndex,
+                               double[] b, int bFromIndex,
+                               int length) {
+        if (length == 0) {
+            return -1;
+        }
+        int aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE);
+        int bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE);
+        int i = vectorizedMismatch(
+                a, aOffset,
+                b, bOffset,
+                length, LOG2_ARRAY_DOUBLE_INDEX_SCALE);
+        if (i >= 0) {
+            // Check if mismatch is not associated with two NaN values
+            if (!Double.isNaN(a[aFromIndex + i]) || !Double.isNaN(b[bFromIndex + i]))
+                return i;
+
+            // Mismatch on two different NaN values that are normalized to match
+            // Fall back to slow mechanism
+            // ISSUE: Consider looping over vectorizedMismatch adjusting ranges
+            // However, requires that returned value be relative to input ranges
+            i++;
+            for (; i < length; i++) {
+                if (Double.doubleToLongBits(a[aFromIndex + i]) != Double.doubleToLongBits(b[bFromIndex + i]))
+                    return i;
+            }
+        }
+
+        return -1;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/Buffer/EqualsCompareTest.java	Wed Dec 20 09:14:52 2017 -0800
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.LongFunction;
+import java.util.stream.IntStream;
+
+/*
+ * @test
+ * @bug 8193085
+ * @summary tests for buffer equals and compare
+ * @run testng EqualsCompareTest
+ */
+
+public class EqualsCompareTest {
+
+    // Maximum width in bits
+    static final int MAX_WIDTH = 512;
+
+    static final Map<Class, Integer> typeToWidth;
+
+    static {
+        typeToWidth = new HashMap<>();
+        typeToWidth.put(byte.class, Byte.SIZE);
+        typeToWidth.put(short.class, Short.SIZE);
+        typeToWidth.put(char.class, Character.SIZE);
+        typeToWidth.put(int.class, Integer.SIZE);
+        typeToWidth.put(long.class, Long.SIZE);
+        typeToWidth.put(float.class, Float.SIZE);
+        typeToWidth.put(double.class, Double.SIZE);
+    }
+
+    static int arraySizeFor(Class<?> type) {
+        assert type.isPrimitive();
+        return 4 * MAX_WIDTH / typeToWidth.get(type);
+    }
+
+    enum BufferKind {
+        HEAP,
+        HEAP_VIEW,
+        DIRECT;
+    }
+
+    static abstract class BufferType<T extends Buffer, E> {
+        final BufferKind k;
+        final Class<?> bufferType;
+        final Class<?> elementType;
+
+        final MethodHandle eq;
+        final MethodHandle cmp;
+
+        final MethodHandle getter;
+        final MethodHandle setter;
+
+        BufferType(BufferKind k, Class<T> bufferType, Class<?> elementType) {
+            this.k = k;
+            this.bufferType = bufferType;
+            this.elementType = elementType;
+
+            var lookup = MethodHandles.lookup();
+            try {
+                eq = lookup.findVirtual(bufferType, "equals", MethodType.methodType(boolean.class, Object.class));
+                cmp = lookup.findVirtual(bufferType, "compareTo", MethodType.methodType(int.class, bufferType));
+
+                getter = lookup.findVirtual(bufferType, "get", MethodType.methodType(elementType, int.class));
+                setter = lookup.findVirtual(bufferType, "put", MethodType.methodType(bufferType, int.class, elementType));
+            }
+            catch (Exception e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return bufferType.getName() + " " + k;
+        }
+
+        T construct(int length) {
+            return construct(length, ByteOrder.BIG_ENDIAN);
+        }
+
+        abstract T construct(int length, ByteOrder bo);
+
+        @SuppressWarnings("unchecked")
+        T slice(T a, int from, int to) {
+            return (T) a.position(from).limit(to).slice();
+        }
+
+        @SuppressWarnings("unchecked")
+        E get(T a, int i) {
+            try {
+                return (E) getter.invoke(a, i);
+            }
+            catch (RuntimeException | Error e) {
+                throw e;
+            }
+            catch (Throwable t) {
+                throw new Error(t);
+            }
+        }
+
+        void set(T a, int i, Object v) {
+            try {
+                setter.invoke(a, i, convert(v));
+            }
+            catch (RuntimeException | Error e) {
+                throw e;
+            }
+            catch (Throwable t) {
+                throw new Error(t);
+            }
+        }
+
+        abstract Object convert(Object o);
+
+        boolean equals(T a, T b) {
+            try {
+                return (boolean) eq.invoke(a, b);
+            }
+            catch (RuntimeException | Error e) {
+                throw e;
+            }
+            catch (Throwable t) {
+                throw new Error(t);
+            }
+        }
+
+        int compare(T a, T b) {
+            try {
+                return (int) cmp.invoke(a, b);
+            }
+            catch (RuntimeException | Error e) {
+                throw e;
+            }
+            catch (Throwable t) {
+                throw new Error(t);
+            }
+        }
+
+        boolean pairWiseEquals(T a, T b) {
+            if (a.remaining() != b.remaining())
+                return false;
+            int p = a.position();
+            for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--)
+                if (!get(a, i).equals(get(b, j)))
+                    return false;
+            return true;
+        }
+
+        static class Bytes extends BufferType<ByteBuffer, Byte> {
+            Bytes(BufferKind k) {
+                super(k, ByteBuffer.class, byte.class);
+            }
+
+            @Override
+            ByteBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length).order(bo);
+                    default:
+                    case HEAP_VIEW:
+                    case HEAP:
+                        return ByteBuffer.allocate(length).order(bo);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? ((Integer) o).byteValue()
+                       : o;
+            }
+        }
+
+        static class Chars extends BufferType<CharBuffer, Character> {
+            Chars(BufferKind k) {
+                super(k, CharBuffer.class, char.class);
+            }
+
+            @Override
+            CharBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Character.BYTES).
+                                order(bo).
+                                asCharBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Character.BYTES).
+                                order(bo).
+                                asCharBuffer();
+                    default:
+                    case HEAP:
+                        return CharBuffer.allocate(length);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? (char) ((Integer) o).intValue()
+                       : o;
+            }
+
+            CharBuffer transformToStringBuffer(CharBuffer c) {
+                char[] chars = new char[c.remaining()];
+                c.get(chars);
+                return CharBuffer.wrap(new String(chars));
+            }
+        }
+
+        static class Shorts extends BufferType<ShortBuffer, Short> {
+            Shorts(BufferKind k) {
+                super(k, ShortBuffer.class, short.class);
+            }
+
+            @Override
+            ShortBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Short.BYTES).
+                                order(bo).
+                                asShortBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Short.BYTES).
+                                order(bo).
+                                asShortBuffer();
+                    default:
+                    case HEAP:
+                        return ShortBuffer.allocate(length);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? ((Integer) o).shortValue()
+                       : o;
+            }
+        }
+
+        static class Ints extends BufferType<IntBuffer, Integer> {
+            Ints(BufferKind k) {
+                super(k, IntBuffer.class, int.class);
+            }
+
+            @Override
+            IntBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Integer.BYTES).
+                                order(bo).
+                                asIntBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Integer.BYTES).
+                                order(bo).
+                                asIntBuffer();
+                    default:
+                    case HEAP:
+                        return IntBuffer.allocate(length);
+                }
+            }
+
+            Object convert(Object o) {
+                return o;
+            }
+        }
+
+        static class Floats extends BufferType<FloatBuffer, Float> {
+            Floats(BufferKind k) {
+                super(k, FloatBuffer.class, float.class);
+            }
+
+            @Override
+            FloatBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Float.BYTES).
+                                order(bo).
+                                asFloatBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Float.BYTES).
+                                order(bo).
+                                asFloatBuffer();
+                    default:
+                    case HEAP:
+                        return FloatBuffer.allocate(length);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? ((Integer) o).floatValue()
+                       : o;
+            }
+
+            @Override
+            boolean pairWiseEquals(FloatBuffer a, FloatBuffer b) {
+                if (a.remaining() != b.remaining())
+                    return false;
+                int p = a.position();
+                for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) {
+                    float av = a.get(i);
+                    float bv = b.get(j);
+                    if (av != bv && (!Float.isNaN(av) || !Float.isNaN(bv)))
+                        return false;
+                }
+                return true;
+            }
+        }
+
+        static class Longs extends BufferType<LongBuffer, Long> {
+            Longs(BufferKind k) {
+                super(k, LongBuffer.class, long.class);
+            }
+
+            @Override
+            LongBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Long.BYTES).
+                                order(bo).
+                                asLongBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Long.BYTES).
+                                order(bo).
+                                asLongBuffer();
+                    default:
+                    case HEAP:
+                        return LongBuffer.allocate(length);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? ((Integer) o).longValue()
+                       : o;
+            }
+        }
+
+        static class Doubles extends BufferType<DoubleBuffer, Double> {
+            Doubles(BufferKind k) {
+                super(k, DoubleBuffer.class, double.class);
+            }
+
+            @Override
+            DoubleBuffer construct(int length, ByteOrder bo) {
+                switch (k) {
+                    case DIRECT:
+                        return ByteBuffer.allocateDirect(length * Double.BYTES).
+                                order(bo).
+                                asDoubleBuffer();
+                    case HEAP_VIEW:
+                        return ByteBuffer.allocate(length * Double.BYTES).
+                                order(bo).
+                                asDoubleBuffer();
+                    default:
+                    case HEAP:
+                        return DoubleBuffer.allocate(length);
+                }
+            }
+
+            @Override
+            Object convert(Object o) {
+                return o instanceof Integer
+                       ? ((Integer) o).doubleValue()
+                       : o;
+            }
+
+            @Override
+            boolean pairWiseEquals(DoubleBuffer a, DoubleBuffer b) {
+                if (a.remaining() != b.remaining())
+                    return false;
+                int p = a.position();
+                for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) {
+                    double av = a.get(i);
+                    double bv = b.get(j);
+                    if (av != bv && (!Double.isNaN(av) || !Double.isNaN(bv)))
+                        return false;
+                }
+                return true;
+            }
+        }
+    }
+
+
+    static Object[][] bufferTypes;
+
+    @DataProvider
+    public static Object[][] bufferTypesProvider() {
+        if (bufferTypes == null) {
+            bufferTypes = new Object[][]{
+                    {new BufferType.Bytes(BufferKind.HEAP)},
+                    {new BufferType.Bytes(BufferKind.DIRECT)},
+                    {new BufferType.Chars(BufferKind.HEAP)},
+                    {new BufferType.Chars(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Chars(BufferKind.DIRECT)},
+                    {new BufferType.Shorts(BufferKind.HEAP)},
+                    {new BufferType.Shorts(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Shorts(BufferKind.DIRECT)},
+                    {new BufferType.Ints(BufferKind.HEAP)},
+                    {new BufferType.Ints(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Ints(BufferKind.DIRECT)},
+                    {new BufferType.Floats(BufferKind.HEAP)},
+                    {new BufferType.Floats(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Floats(BufferKind.DIRECT)},
+                    {new BufferType.Longs(BufferKind.HEAP)},
+                    {new BufferType.Longs(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Longs(BufferKind.DIRECT)},
+                    {new BufferType.Doubles(BufferKind.HEAP)},
+                    {new BufferType.Doubles(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Doubles(BufferKind.DIRECT)},
+            };
+        }
+        return bufferTypes;
+    }
+
+
+    static Object[][] floatbufferTypes;
+
+    @DataProvider
+    public static Object[][] floatBufferTypesProvider() {
+        if (floatbufferTypes == null) {
+            LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb);
+            LongFunction<Object> bToD = Double::longBitsToDouble;
+
+            floatbufferTypes = new Object[][]{
+                    // canonical and non-canonical NaNs
+                    // If conversion is a signalling NaN it may be subject to conversion to a
+                    // quiet NaN on some processors, even if a copy is performed
+                    // The tests assume that if conversion occurs it does not convert to the
+                    // canonical NaN
+                    new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x7fc00000L, 0x7f800001L, bTof},
+                    new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x7fc00000L, 0x7f800001L, bTof},
+                    new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x7fc00000L, 0x7f800001L, bTof},
+                    new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD},
+                    new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD},
+                    new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD},
+
+                    // +0.0 and -0.0
+                    new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x0L, 0x80000000L, bTof},
+                    new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x0L, 0x80000000L, bTof},
+                    new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x0L, 0x80000000L, bTof},
+                    new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x0L, 0x8000000000000000L, bToD},
+                    new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x0L, 0x8000000000000000L, bToD},
+                    new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x0L, 0x8000000000000000L, bToD},
+            };
+        }
+        return floatbufferTypes;
+    }
+
+
+    static Object[][] charBufferTypes;
+
+    @DataProvider
+    public static Object[][] charBufferTypesProvider() {
+        if (charBufferTypes == null) {
+            charBufferTypes = new Object[][]{
+                    {new BufferType.Chars(BufferKind.HEAP)},
+                    {new BufferType.Chars(BufferKind.HEAP_VIEW)},
+                    {new BufferType.Chars(BufferKind.DIRECT)},
+            };
+        }
+        return charBufferTypes;
+    }
+
+
+    // Tests all primitive buffers
+    @Test(dataProvider = "bufferTypesProvider")
+    <E>
+    void testBuffers(BufferType<Buffer, E> bufferType) {
+        // Test with buffers of the same byte order (BE)
+        BiFunction<BufferType<Buffer, E>, Integer, Buffer> constructor = (at, s) -> {
+            Buffer a = at.construct(s);
+            for (int x = 0; x < s; x++) {
+                at.set(a, x, x % 8);
+            }
+            return a;
+        };
+
+        testBufferType(bufferType, constructor, constructor);
+
+        // Test with buffers of different byte order
+        if (bufferType.elementType != byte.class &&
+            (bufferType.k == BufferKind.HEAP_VIEW ||
+             bufferType.k == BufferKind.DIRECT)) {
+
+            BiFunction<BufferType<Buffer, E>, Integer, Buffer> leConstructor = (at, s) -> {
+                Buffer a = at.construct(s, ByteOrder.LITTLE_ENDIAN);
+                for (int x = 0; x < s; x++) {
+                    at.set(a, x, x % 8);
+                }
+                return a;
+            };
+            testBufferType(bufferType, constructor, leConstructor);
+        }
+    }
+
+    // Tests float and double buffers with edge-case values (NaN, -0.0, +0.0)
+    @Test(dataProvider = "floatBufferTypesProvider")
+    public void testFloatBuffers(
+            BufferType<Buffer, Float> bufferType,
+            long rawBitsA, long rawBitsB,
+            LongFunction<Object> bitsToFloat) {
+        Object av = bitsToFloat.apply(rawBitsA);
+        Object bv = bitsToFloat.apply(rawBitsB);
+
+        BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allAs = (at, s) -> {
+            Buffer b = at.construct(s);
+            for (int x = 0; x < s; x++) {
+                at.set(b, x, av);
+            }
+            return b;
+        };
+
+        BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allBs = (at, s) -> {
+            Buffer b = at.construct(s);
+            for (int x = 0; x < s; x++) {
+                at.set(b, x, bv);
+            }
+            return b;
+        };
+
+        BiFunction<BufferType<Buffer, Float>, Integer, Buffer> halfBs = (at, s) -> {
+            Buffer b = at.construct(s);
+            for (int x = 0; x < s / 2; x++) {
+                at.set(b, x, bv);
+            }
+            for (int x = s / 2; x < s; x++) {
+                at.set(b, x, 1);
+            }
+            return b;
+        };
+
+        // Sanity check
+        int size = arraySizeFor(bufferType.elementType);
+        Assert.assertTrue(bufferType.pairWiseEquals(allAs.apply(bufferType, size),
+                                                    allBs.apply(bufferType, size)));
+        Assert.assertTrue(bufferType.equals(allAs.apply(bufferType, size),
+                                            allBs.apply(bufferType, size)));
+
+        testBufferType(bufferType, allAs, allBs);
+        testBufferType(bufferType, allAs, halfBs);
+    }
+
+    // Tests CharBuffer for region sources and CharSequence sources
+    @Test(dataProvider = "charBufferTypesProvider")
+    public void testCharBuffers(BufferType.Chars charBufferType) {
+
+        BiFunction<BufferType.Chars, Integer, CharBuffer> constructor = (at, s) -> {
+            CharBuffer a = at.construct(s);
+            for (int x = 0; x < s; x++) {
+                at.set(a, x, x % 8);
+            }
+            return a;
+        };
+
+        BiFunction<BufferType.Chars, Integer, CharBuffer> constructorX = constructor.
+                andThen(charBufferType::transformToStringBuffer);
+
+        testBufferType(charBufferType, constructor, constructorX);
+    }
+
+
+    <B extends Buffer, E, BT extends BufferType<B, E>>
+    void testBufferType(BT bt,
+                        BiFunction<BT, Integer, B> aConstructor,
+                        BiFunction<BT, Integer, B> bConstructor) {
+        int n = arraySizeFor(bt.elementType);
+
+        for (int s : ranges(0, n)) {
+            B a = aConstructor.apply(bt, s);
+            B b = bConstructor.apply(bt, s);
+
+            for (int aFrom : ranges(0, s)) {
+                for (int aTo : ranges(aFrom, s)) {
+                    int aLength = aTo - aFrom;
+
+                    B as = aLength != s
+                           ? bt.slice(a, aFrom, aTo)
+                           : a;
+
+                    for (int bFrom : ranges(0, s)) {
+                        for (int bTo : ranges(bFrom, s)) {
+                            int bLength = bTo - bFrom;
+
+                            B bs = bLength != s
+                                   ? bt.slice(b, bFrom, bTo)
+                                   : b;
+
+                            boolean eq = bt.pairWiseEquals(as, bs);
+                            Assert.assertEquals(bt.equals(as, bs), eq);
+                            Assert.assertEquals(bt.equals(bs, as), eq);
+                            if (eq) {
+                                Assert.assertEquals(bt.compare(as, bs), 0);
+                                Assert.assertEquals(bt.compare(bs, as), 0);
+                            }
+                            else {
+                                int aCb = bt.compare(as, bs);
+                                int bCa = bt.compare(bs, as);
+                                int v = Integer.signum(aCb) * Integer.signum(bCa);
+                                Assert.assertTrue(v == -1);
+                            }
+                        }
+                    }
+
+                    if (aLength > 0 && !a.isReadOnly()) {
+                        for (int i = aFrom; i < aTo; i++) {
+                            B c = aConstructor.apply(bt, a.capacity());
+                            B cs = aLength != s
+                                   ? bt.slice(c, aFrom, aTo)
+                                   : c;
+
+                            // Create common prefix with a length of i - aFrom
+                            bt.set(c, i, -1);
+
+                            Assert.assertFalse(bt.equals(c, a));
+
+                            int cCa = bt.compare(cs, as);
+                            int aCc = bt.compare(as, cs);
+                            int v = Integer.signum(cCa) * Integer.signum(aCc);
+                            Assert.assertTrue(v == -1);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static int[] ranges(int from, int to) {
+        int width = to - from;
+        switch (width) {
+            case 0:
+                return new int[]{};
+            case 1:
+                return new int[]{from, to};
+            case 2:
+                return new int[]{from, from + 1, to};
+            case 3:
+                return new int[]{from, from + 1, from + 2, to};
+            default:
+                return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to)
+                        .filter(i -> i >= from && i <= to)
+                        .distinct().toArray();
+        }
+    }
+}