8193085: Vectorize the nio Buffer equals and compareTo implementations

Reviewed-by: alanb
This commit is contained in:
Paul Sandoz 2017-12-20 09:14:52 -08:00
parent 038b5f571c
commit fb9db6b02d
13 changed files with 1122 additions and 195 deletions

View File

@ -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") \
\

View File

@ -63,38 +63,38 @@ class Bits { // package-private
// -- 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 @@ class Bits { // package-private
// 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 @@ class Bits { // package-private
// 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 @@ class Bits { // package-private
// 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 @@ class Bits { // package-private
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 @@ class Bits { // package-private
}
@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();
}
};
}

View File

@ -26,6 +26,7 @@
package java.nio;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.Unsafe;
import java.util.Spliterator;
@ -181,6 +182,8 @@ import java.util.Spliterator;
*/
public abstract class Buffer {
// Cached unsafe-access object
static final Unsafe UNSAFE = Bits.unsafe();
/**
* The characteristics of Spliterators that traverse and split elements
@ -616,6 +619,14 @@ public abstract class Buffer {
// -- 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

View File

@ -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;
}
}

View File

@ -36,9 +36,6 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
#if[rw]
// Cached unsafe-access object
private static final Unsafe unsafe = Bits.unsafe();
protected final ByteBuffer bb;
#end[rw]
@ -74,6 +71,11 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
#end[rw]
}
@Override
Object base() {
return bb.hb;
}
public $Type$Buffer slice() {
int pos = this.position();
int lim = this.limit();
@ -117,20 +119,20 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
}
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 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
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 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
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 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
#end[boL]
}
#if[char]
ByteOrder charRegionOrder() {
return order();
}
#end[char]
}

View File

@ -32,7 +32,7 @@ class XXX {
#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 @@ class XXX {
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 @@ class XXX {
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,

View File

@ -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 @@ class Direct$Type$Buffer$RW$$BO$
#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 @@ class Direct$Type$Buffer$RW$$BO$
implements Runnable
{
private static Unsafe unsafe = Unsafe.getUnsafe();
private long address;
private long size;
private int capacity;
@ -91,7 +85,7 @@ class Direct$Type$Buffer$RW$$BO$
// Paranoia
return;
}
unsafe.freeMemory(address);
UNSAFE.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
@ -124,12 +118,12 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
#end[rw]
}
@Override
Object base() {
return null;
}
public $Type$Buffer slice() {
int pos = this.position();
int lim = this.limit();
@ -258,16 +257,16 @@ class Direct$Type$Buffer$RW$$BO$
}
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 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
unsafe.copyMemory(null,
UNSAFE.copyMemory(null,
ix(pos),
dst,
dstOffset,
@ -312,7 +311,7 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
unsafe.copyMemory(src,
UNSAFE.copyMemory(src,
srcOffset,
null,
ix(pos),
@ -413,7 +412,7 @@ class Direct$Type$Buffer$RW$$BO$
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 @@ class Direct$Type$Buffer$RW$$BO$
#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]

View File

@ -27,8 +27,6 @@
package java.nio;
import jdk.internal.misc.Unsafe;
/**
#if[rw]
* A read/write Heap$Type$Buffer.
@ -43,6 +41,11 @@ import jdk.internal.misc.Unsafe;
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
#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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
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 @@ class Heap$Type$Buffer$RW$
public ByteOrder order() {
return ByteOrder.nativeOrder();
}
#end[!byte]
#if[char]
ByteOrder charRegionOrder() {
return order();
}
#end[char]
}

View File

@ -127,4 +127,30 @@ class StringCharBuffer // package-private
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();
}
}

View File

@ -36,6 +36,8 @@ import java.util.stream.StreamSupport;
import java.util.stream.$Streamtype$Stream;
#end[streamableType]
import jdk.internal.util.ArraysSupport;
/**
* $A$ $type$ buffer.
*
@ -287,6 +289,11 @@ public abstract class $Type$Buffer
this(mark, pos, lim, cap, null, 0);
}
@Override
Object base() {
return hb;
}
#if[byte]
/**
@ -1297,19 +1304,9 @@ public abstract class $Type$Buffer
$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 @@ public abstract class $Type$Buffer
* 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 @@ public abstract class $Type$Buffer
#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

View File

@ -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;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
package jdk.internal.util;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.Unsafe;
@ -50,19 +50,19 @@ import jdk.internal.misc.Unsafe;
* responsibility of the caller (direct or otherwise) to perform such checks
* before calling this method.
*/
class ArraysSupport {
public 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);
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);
@ -107,10 +107,10 @@ class ArraysSupport {
* the tail of the two arrays.
*/
@HotSpotIntrinsicCandidate
static int vectorizedMismatch(Object a, long aOffset,
Object b, long bOffset,
int length,
int log2ArrayIndexScale) {
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)
@ -161,9 +161,9 @@ class ArraysSupport {
// Booleans
// Each boolean element takes up one byte
static int mismatch(boolean[] a,
boolean[] b,
int length) {
public static int mismatch(boolean[] a,
boolean[] b,
int length) {
int i = 0;
if (length > 7) {
i = vectorizedMismatch(
@ -181,9 +181,9 @@ class ArraysSupport {
return -1;
}
static int mismatch(boolean[] a, int aFromIndex,
boolean[] b, int bFromIndex,
int length) {
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;
@ -219,9 +219,9 @@ class ArraysSupport {
* 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) {
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
@ -264,9 +264,9 @@ class ArraysSupport {
* 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) {
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
@ -295,9 +295,9 @@ class ArraysSupport {
// Chars
static int mismatch(char[] a,
char[] b,
int length) {
public static int mismatch(char[] a,
char[] b,
int length) {
int i = 0;
if (length > 3) {
i = vectorizedMismatch(
@ -315,9 +315,9 @@ class ArraysSupport {
return -1;
}
static int mismatch(char[] a, int aFromIndex,
char[] b, int bFromIndex,
int length) {
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);
@ -340,9 +340,9 @@ class ArraysSupport {
// Shorts
static int mismatch(short[] a,
short[] b,
int length) {
public static int mismatch(short[] a,
short[] b,
int length) {
int i = 0;
if (length > 3) {
i = vectorizedMismatch(
@ -360,9 +360,9 @@ class ArraysSupport {
return -1;
}
static int mismatch(short[] a, int aFromIndex,
short[] b, int bFromIndex,
int length) {
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);
@ -385,9 +385,9 @@ class ArraysSupport {
// Ints
static int mismatch(int[] a,
int[] b,
int length) {
public static int mismatch(int[] a,
int[] b,
int length) {
int i = 0;
if (length > 1) {
i = vectorizedMismatch(
@ -405,9 +405,9 @@ class ArraysSupport {
return -1;
}
static int mismatch(int[] a, int aFromIndex,
int[] b, int bFromIndex,
int length) {
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);
@ -430,15 +430,15 @@ class ArraysSupport {
// Floats
static int mismatch(float[] a,
float[] b,
int length) {
public 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) {
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);
@ -475,9 +475,9 @@ class ArraysSupport {
// Long
static int mismatch(long[] a,
long[] b,
int length) {
public static int mismatch(long[] a,
long[] b,
int length) {
if (length == 0) {
return -1;
}
@ -488,9 +488,9 @@ class ArraysSupport {
return i >= 0 ? i : -1;
}
static int mismatch(long[] a, int aFromIndex,
long[] b, int bFromIndex,
int length) {
public static int mismatch(long[] a, int aFromIndex,
long[] b, int bFromIndex,
int length) {
if (length == 0) {
return -1;
}
@ -506,15 +506,15 @@ class ArraysSupport {
// Double
static int mismatch(double[] a,
double[] b,
int length) {
public 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) {
public static int mismatch(double[] a, int aFromIndex,
double[] b, int bFromIndex,
int length) {
if (length == 0) {
return -1;
}

View File

@ -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();
}
}
}