src/java.base/share/classes/java/util/ArraysSupport.java
changeset 47216 71c04702a3d5
parent 35255 6f0bf592d149
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/util/ArraysSupport.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,545 @@
+/*
+ * 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;
+    }
+}