8193085: Vectorize the nio Buffer equals and compareTo implementations
Reviewed-by: alanb
--- 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();
+ }
+ }
+}