From 30289cc48c36bd7a59d6c707118d74383bf5f378 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 05:16:50 +0800 Subject: [PATCH 01/12] Reimplement ByteArray and ByteArrayLittleEndian with Unsafe --- .../classes/jdk/internal/util/ByteArray.java | 151 +++++++++--------- .../internal/util/ByteArrayLittleEndian.java | 143 ++++++++--------- 2 files changed, 149 insertions(+), 145 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 94395df2c344b..f19b7e2d68628 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -25,13 +25,15 @@ package jdk.internal.util; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.ForceInline; + +import java.util.List; +import java.util.function.BiFunction; /** * Utility methods for packing/unpacking primitive values in/out of byte arrays - * using {@linkplain ByteOrder#BIG_ENDIAN big endian order} (aka. "network order"). + * using {@linkplain java.nio.ByteOrder#BIG_ENDIAN big endian order} (aka. "network order"). *

* All methods in this class will throw an {@linkplain NullPointerException} if {@code null} is * passed in as a method parameter for a byte array. @@ -41,12 +43,15 @@ public final class ByteArray { private ByteArray() { } - private static final VarHandle SHORT = create(short[].class); - private static final VarHandle CHAR = create(char[].class); - private static final VarHandle INT = create(int[].class); - private static final VarHandle FLOAT = create(float[].class); - private static final VarHandle LONG = create(long[].class); - private static final VarHandle DOUBLE = create(double[].class); + private static final BiFunction, ArrayIndexOutOfBoundsException> + OOBEF = Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new); + + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + @ForceInline + static long arrayOffset(byte[] array, int typeBytes, int offset) { + return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, OOBEF) + Unsafe.ARRAY_BYTE_BASE_OFFSET; + } /* * Methods for unpacking primitive values from byte arrays starting at @@ -62,6 +67,7 @@ private ByteArray() { * the range [0, array.length - 1] * @see #setBoolean(byte[], int, boolean) */ + @ForceInline public static boolean getBoolean(byte[] array, int offset) { return array[offset] != 0; } @@ -78,8 +84,12 @@ public static boolean getBoolean(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setChar(byte[], int, char) */ + @ForceInline public static char getChar(byte[] array, int offset) { - return (char) CHAR.get(array, offset); + return UNSAFE.getCharUnaligned( + array, + arrayOffset(array, Character.BYTES, offset), + true); } /** @@ -95,8 +105,12 @@ public static char getChar(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setShort(byte[], int, short) */ + @ForceInline public static short getShort(byte[] array, int offset) { - return (short) SHORT.get(array, offset); + return UNSAFE.getShortUnaligned( + array, + arrayOffset(array, Short.BYTES, offset), + true); } /** @@ -112,8 +126,9 @@ public static short getShort(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setUnsignedShort(byte[], int, int) */ + @ForceInline public static int getUnsignedShort(byte[] array, int offset) { - return Short.toUnsignedInt((short) SHORT.get(array, offset)); + return Short.toUnsignedInt(getShort(array, offset)); } /** @@ -128,8 +143,12 @@ public static int getUnsignedShort(byte[] array, int offset) { * the range [0, array.length - 4] * @see #setInt(byte[], int, int) */ + @ForceInline public static int getInt(byte[] array, int offset) { - return (int) INT.get(array, offset); + return UNSAFE.getIntUnaligned( + array, + arrayOffset(array, Integer.BYTES, offset), + true); } /** @@ -146,30 +165,11 @@ public static int getInt(byte[] array, int offset) { * the range [0, array.length - 4] * @see #setFloat(byte[], int, float) */ + @ForceInline public static float getFloat(byte[] array, int offset) { // Using Float.intBitsToFloat collapses NaN values to a single // "canonical" NaN value - return Float.intBitsToFloat((int) INT.get(array, offset)); - } - - /** - * {@return a {@code float} from the provided {@code array} at the given {@code offset} - * using big endian order}. - *

- * Variants of {@linkplain Float#NaN } values are silently read according - * to their bit patterns. - *

- * There are no access alignment requirements. - * - * @param array to get a value from. - * @param offset where extraction in the array should begin - * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] - * @see #setFloatRaw(byte[], int, float) - */ - public static float getFloatRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (float) FLOAT.get(array, offset); + return Float.intBitsToFloat(getInt(array, offset)); } /** @@ -184,8 +184,12 @@ public static float getFloatRaw(byte[] array, int offset) { * the range [0, array.length - 8] * @see #setLong(byte[], int, long) */ + @ForceInline public static long getLong(byte[] array, int offset) { - return (long) LONG.get(array, offset); + return UNSAFE.getLongUnaligned( + array, + arrayOffset(array, Long.BYTES, offset), + true); } /** @@ -202,30 +206,11 @@ public static long getLong(byte[] array, int offset) { * the range [0, array.length - 8] * @see #setDouble(byte[], int, double) */ + @ForceInline public static double getDouble(byte[] array, int offset) { // Using Double.longBitsToDouble collapses NaN values to a single // "canonical" NaN value - return Double.longBitsToDouble((long) LONG.get(array, offset)); - } - - /** - * {@return a {@code double} from the provided {@code array} at the given {@code offset} - * using big endian order}. - *

- * Variants of {@linkplain Double#NaN } values are silently read according to - * their bit patterns. - *

- * There are no access alignment requirements. - * - * @param array to get a value from. - * @param offset where extraction in the array should begin - * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 8] - * @see #setDoubleRaw(byte[], int, double) - */ - public static double getDoubleRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (double) DOUBLE.get(array, offset); + return Double.longBitsToDouble(getLong(array, offset)); } /* @@ -244,6 +229,7 @@ public static double getDoubleRaw(byte[] array, int offset) { * the range [0, array.length] * @see #getBoolean(byte[], int) */ + @ForceInline public static void setBoolean(byte[] array, int offset, boolean value) { array[offset] = (byte) (value ? 1 : 0); } @@ -261,8 +247,13 @@ public static void setBoolean(byte[] array, int offset, boolean value) { * the range [0, array.length - 2] * @see #getChar(byte[], int) */ + @ForceInline public static void setChar(byte[] array, int offset, char value) { - CHAR.set(array, offset, value); + UNSAFE.putCharUnaligned( + array, + arrayOffset(array, Character.BYTES, offset), + value, + true); } /** @@ -278,8 +269,13 @@ public static void setChar(byte[] array, int offset, char value) { * the range [0, array.length - 2] * @see #getShort(byte[], int) */ + @ForceInline public static void setShort(byte[] array, int offset, short value) { - SHORT.set(array, offset, value); + UNSAFE.putShortUnaligned( + array, + arrayOffset(array, Short.BYTES, offset), + value, + true); } /** @@ -295,8 +291,9 @@ public static void setShort(byte[] array, int offset, short value) { * the range [0, array.length - 2] * @see #getUnsignedShort(byte[], int) */ + @ForceInline public static void setUnsignedShort(byte[] array, int offset, int value) { - SHORT.set(array, offset, (short) (char) value); + setShort(array, offset, (short) (char) value); } /** @@ -312,8 +309,13 @@ public static void setUnsignedShort(byte[] array, int offset, int value) { * the range [0, array.length - 4] * @see #getInt(byte[], int) */ + @ForceInline public static void setInt(byte[] array, int offset, int value) { - INT.set(array, offset, value); + UNSAFE.putIntUnaligned( + array, + arrayOffset(array, Integer.BYTES, offset), + value, + true); } /** @@ -331,10 +333,11 @@ public static void setInt(byte[] array, int offset, int value) { * the range [0, array.length - 2] * @see #getFloat(byte[], int) */ + @ForceInline public static void setFloat(byte[] array, int offset, float value) { // Using Float.floatToIntBits collapses NaN values to a single // "canonical" NaN value - INT.set(array, offset, Float.floatToIntBits(value)); + setInt(array, offset, Float.floatToIntBits(value)); } /** @@ -351,11 +354,12 @@ public static void setFloat(byte[] array, int offset, float value) { * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside * the range [0, array.length - 2] - * @see #getFloatRaw(byte[], int) + * @see #getFloat(byte[], int) */ + @ForceInline public static void setFloatRaw(byte[] array, int offset, float value) { // Just sets the bits as they are - FLOAT.set(array, offset, value); + setInt(array, offset, Float.floatToRawIntBits(value)); } /** @@ -371,8 +375,13 @@ public static void setFloatRaw(byte[] array, int offset, float value) { * the range [0, array.length - 4] * @see #getLong(byte[], int) */ + @ForceInline public static void setLong(byte[] array, int offset, long value) { - LONG.set(array, offset, value); + UNSAFE.putLongUnaligned( + array, + arrayOffset(array, Long.BYTES, offset), + value, + true); } /** @@ -390,10 +399,11 @@ public static void setLong(byte[] array, int offset, long value) { * the range [0, array.length - 2] * @see #getDouble(byte[], int) */ + @ForceInline public static void setDouble(byte[] array, int offset, double value) { // Using Double.doubleToLongBits collapses NaN values to a single // "canonical" NaN value - LONG.set(array, offset, Double.doubleToLongBits(value)); + setLong(array, offset, Double.doubleToLongBits(value)); } /** @@ -410,15 +420,12 @@ public static void setDouble(byte[] array, int offset, double value) { * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside * the range [0, array.length - 2] - * @see #getDoubleRaw(byte[], int) + * @see #getDouble(byte[], int) */ + @ForceInline public static void setDoubleRaw(byte[] array, int offset, double value) { // Just sets the bits as they are - DOUBLE.set(array, offset, value); - } - - private static VarHandle create(Class viewArrayClass) { - return MethodHandles.byteArrayViewVarHandle(viewArrayClass, ByteOrder.BIG_ENDIAN); + setLong(array, offset, Double.doubleToRawLongBits(value)); } } diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index fcd7ca2b9bfa9..a7dec6cc0cdc0 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java @@ -25,13 +25,15 @@ package jdk.internal.util; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.ForceInline; + +import static jdk.internal.util.ByteArray.arrayOffset; +import static jdk.internal.util.ByteArray.UNSAFE; /** * Utility methods for packing/unpacking primitive values in/out of byte arrays - * using {@linkplain ByteOrder#LITTLE_ENDIAN little endian order}. + * using {@linkplain java.nio.ByteOrder#LITTLE_ENDIAN little endian order}. *

* All methods in this class will throw an {@linkplain NullPointerException} if {@code null} is * passed in as a method parameter for a byte array. @@ -41,13 +43,6 @@ public final class ByteArrayLittleEndian { private ByteArrayLittleEndian() { } - private static final VarHandle SHORT = createLittleEndian(short[].class); - private static final VarHandle CHAR = createLittleEndian(char[].class); - private static final VarHandle INT = createLittleEndian(int[].class); - private static final VarHandle FLOAT = createLittleEndian(float[].class); - private static final VarHandle LONG = createLittleEndian(long[].class); - private static final VarHandle DOUBLE = createLittleEndian(double[].class); - /* * Methods for unpacking primitive values from byte arrays starting at * a given offset. @@ -62,6 +57,7 @@ private ByteArrayLittleEndian() { * the range [0, array.length - 1] * @see #setBoolean(byte[], int, boolean) */ + @ForceInline public static boolean getBoolean(byte[] array, int offset) { return array[offset] != 0; } @@ -78,8 +74,12 @@ public static boolean getBoolean(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setChar(byte[], int, char) */ + @ForceInline public static char getChar(byte[] array, int offset) { - return (char) CHAR.get(array, offset); + return UNSAFE.getCharUnaligned( + array, + arrayOffset(array, Character.BYTES, offset), + false); } /** @@ -95,8 +95,12 @@ public static char getChar(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setShort(byte[], int, short) */ + @ForceInline public static short getShort(byte[] array, int offset) { - return (short) SHORT.get(array, offset); + return UNSAFE.getShortUnaligned( + array, + arrayOffset(array, Short.BYTES, offset), + false); } /** @@ -112,8 +116,9 @@ public static short getShort(byte[] array, int offset) { * the range [0, array.length - 2] * @see #setUnsignedShort(byte[], int, int) */ + @ForceInline public static int getUnsignedShort(byte[] array, int offset) { - return Short.toUnsignedInt((short) SHORT.get(array, offset)); + return Short.toUnsignedInt(getShort(array, offset)); } /** @@ -128,8 +133,12 @@ public static int getUnsignedShort(byte[] array, int offset) { * the range [0, array.length - 4] * @see #setInt(byte[], int, int) */ + @ForceInline public static int getInt(byte[] array, int offset) { - return (int) INT.get(array, offset); + return UNSAFE.getIntUnaligned( + array, + arrayOffset(array, Integer.BYTES, offset), + false); } /** @@ -146,30 +155,11 @@ public static int getInt(byte[] array, int offset) { * the range [0, array.length - 4] * @see #setFloat(byte[], int, float) */ + @ForceInline public static float getFloat(byte[] array, int offset) { // Using Float.intBitsToFloat collapses NaN values to a single // "canonical" NaN value - return Float.intBitsToFloat((int) INT.get(array, offset)); - } - - /** - * {@return a {@code float} from the provided {@code array} at the given {@code offset} - * using little endian order}. - *

- * Variants of {@linkplain Float#NaN } values are silently read according - * to their bit patterns. - *

- * There are no access alignment requirements. - * - * @param array to get a value from. - * @param offset where extraction in the array should begin - * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] - * @see #setFloatRaw(byte[], int, float) - */ - public static float getFloatRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (float) FLOAT.get(array, offset); + return Float.intBitsToFloat(getInt(array, offset)); } /** @@ -184,8 +174,12 @@ public static float getFloatRaw(byte[] array, int offset) { * the range [0, array.length - 8] * @see #setLong(byte[], int, long) */ + @ForceInline public static long getLong(byte[] array, int offset) { - return (long) LONG.get(array, offset); + return UNSAFE.getLongUnaligned( + array, + arrayOffset(array, Long.BYTES, offset), + false); } /** @@ -202,30 +196,11 @@ public static long getLong(byte[] array, int offset) { * the range [0, array.length - 8] * @see #setDouble(byte[], int, double) */ + @ForceInline public static double getDouble(byte[] array, int offset) { // Using Double.longBitsToDouble collapses NaN values to a single // "canonical" NaN value - return Double.longBitsToDouble((long) LONG.get(array, offset)); - } - - /** - * {@return a {@code double} from the provided {@code array} at the given {@code offset} - * using little endian order}. - *

- * Variants of {@linkplain Double#NaN } values are silently read according to - * their bit patterns. - *

- * There are no access alignment requirements. - * - * @param array to get a value from. - * @param offset where extraction in the array should begin - * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 8] - * @see #setDoubleRaw(byte[], int, double) - */ - public static double getDoubleRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (double) DOUBLE.get(array, offset); + return Double.longBitsToDouble(getLong(array, offset)); } /* @@ -244,6 +219,7 @@ public static double getDoubleRaw(byte[] array, int offset) { * the range [0, array.length] * @see #getBoolean(byte[], int) */ + @ForceInline public static void setBoolean(byte[] array, int offset, boolean value) { array[offset] = (byte) (value ? 1 : 0); } @@ -261,8 +237,13 @@ public static void setBoolean(byte[] array, int offset, boolean value) { * the range [0, array.length - 2] * @see #getChar(byte[], int) */ + @ForceInline public static void setChar(byte[] array, int offset, char value) { - CHAR.set(array, offset, value); + UNSAFE.putCharUnaligned( + array, + arrayOffset(array, Character.BYTES, offset), + value, + false); } /** @@ -278,8 +259,13 @@ public static void setChar(byte[] array, int offset, char value) { * the range [0, array.length - 2] * @see #getShort(byte[], int) */ + @ForceInline public static void setShort(byte[] array, int offset, short value) { - SHORT.set(array, offset, value); + UNSAFE.putShortUnaligned( + array, + arrayOffset(array, Short.BYTES, offset), + value, + false); } /** @@ -295,8 +281,9 @@ public static void setShort(byte[] array, int offset, short value) { * the range [0, array.length - 2] * @see #getUnsignedShort(byte[], int) */ + @ForceInline public static void setUnsignedShort(byte[] array, int offset, int value) { - SHORT.set(array, offset, (short) (char) value); + setShort(array, offset, (short) (char) value); } /** @@ -312,8 +299,13 @@ public static void setUnsignedShort(byte[] array, int offset, int value) { * the range [0, array.length - 4] * @see #getInt(byte[], int) */ + @ForceInline public static void setInt(byte[] array, int offset, int value) { - INT.set(array, offset, value); + UNSAFE.putIntUnaligned( + array, + arrayOffset(array, Integer.BYTES, offset), + value, + false); } /** @@ -331,10 +323,11 @@ public static void setInt(byte[] array, int offset, int value) { * the range [0, array.length - 2] * @see #getFloat(byte[], int) */ + @ForceInline public static void setFloat(byte[] array, int offset, float value) { // Using Float.floatToIntBits collapses NaN values to a single // "canonical" NaN value - INT.set(array, offset, Float.floatToIntBits(value)); + setInt(array, offset, Float.floatToIntBits(value)); } /** @@ -351,11 +344,12 @@ public static void setFloat(byte[] array, int offset, float value) { * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside * the range [0, array.length - 2] - * @see #getFloatRaw(byte[], int) + * @see #getFloat(byte[], int) */ + @ForceInline public static void setFloatRaw(byte[] array, int offset, float value) { // Just sets the bits as they are - FLOAT.set(array, offset, value); + setInt(array, offset, Float.floatToRawIntBits(value)); } /** @@ -371,8 +365,13 @@ public static void setFloatRaw(byte[] array, int offset, float value) { * the range [0, array.length - 4] * @see #getLong(byte[], int) */ + @ForceInline public static void setLong(byte[] array, int offset, long value) { - LONG.set(array, offset, value); + UNSAFE.putLongUnaligned( + array, + arrayOffset(array, Long.BYTES, offset), + value, + false); } /** @@ -390,10 +389,11 @@ public static void setLong(byte[] array, int offset, long value) { * the range [0, array.length - 2] * @see #getDouble(byte[], int) */ + @ForceInline public static void setDouble(byte[] array, int offset, double value) { // Using Double.doubleToLongBits collapses NaN values to a single // "canonical" NaN value - LONG.set(array, offset, Double.doubleToLongBits(value)); + setLong(array, offset, Double.doubleToLongBits(value)); } /** @@ -410,15 +410,12 @@ public static void setDouble(byte[] array, int offset, double value) { * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside * the range [0, array.length - 2] - * @see #getDoubleRaw(byte[], int) + * @see #getDouble(byte[], int) */ + @ForceInline public static void setDoubleRaw(byte[] array, int offset, double value) { // Just sets the bits as they are - DOUBLE.set(array, offset, value); - } - - private static VarHandle createLittleEndian(Class viewArrayClass) { - return MethodHandles.byteArrayViewVarHandle(viewArrayClass, ByteOrder.LITTLE_ENDIAN); + setLong(array, offset, Double.doubleToRawLongBits(value)); } } From ffcf50899a8dcbe19ef3a5d0cef41f14dd6d2d35 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 14:55:34 +0800 Subject: [PATCH 02/12] get/set unsigned int --- .../classes/jdk/internal/util/ByteArray.java | 39 ++++++++++++++++++- .../internal/util/ByteArrayLittleEndian.java | 36 +++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index f19b7e2d68628..789f8ebb6eed4 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -50,7 +50,8 @@ private ByteArray() { @ForceInline static long arrayOffset(byte[] array, int typeBytes, int offset) { - return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, OOBEF) + Unsafe.ARRAY_BYTE_BASE_OFFSET; + return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, OOBEF) + + Unsafe.ARRAY_BYTE_BASE_OFFSET; } /* @@ -151,6 +152,24 @@ public static int getInt(byte[] array, int offset) { true); } + /** + * {@return an {@code unsigned int} from the provided {@code array} at the given {@code offset} + * using big endian order}. + *

+ * There are no access alignment requirements. + * + * @param array to get a value from. + * @param offset where extraction in the array should begin + * @return an {@code long} representing an unsigned int from the array + * @throws IndexOutOfBoundsException if the provided {@code offset} is outside + * the range [0, array.length - 4] + * @see #setUnsignedInt(byte[], int, long) + */ + @ForceInline + public static long getUnsignedInt(byte[] array, int offset) { + return Integer.toUnsignedLong(getInt(array, offset)); + } + /** * {@return a {@code float} from the provided {@code array} at the given {@code offset} * using big endian order}. @@ -318,6 +337,24 @@ public static void setInt(byte[] array, int offset, int value) { true); } + /** + * Sets (writes) the provided {@code value} using big endian order into + * the provided {@code array} beginning at the given {@code offset}. + *

+ * There are no access alignment requirements. + * + * @param array to set (write) a value into + * @param offset where setting (writing) in the array should begin + * @param value value to set in the array + * @throws IndexOutOfBoundsException if the provided {@code offset} is outside + * the range [0, array.length - 4] + * @see #getUnsignedInt(byte[], int) + */ + @ForceInline + public static void setUnsignedInt(byte[] array, int offset, long value) { + setInt(array, offset, (int) value); + } + /** * Sets (writes) the provided {@code value} using big endian order into * the provided {@code array} beginning at the given {@code offset}. diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index a7dec6cc0cdc0..736b2bb879091 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java @@ -141,6 +141,24 @@ public static int getInt(byte[] array, int offset) { false); } + /** + * {@return an {@code unsigned int} from the provided {@code array} at the given {@code offset} + * using big endian order}. + *

+ * There are no access alignment requirements. + * + * @param array to get a value from. + * @param offset where extraction in the array should begin + * @return an {@code long} representing an unsigned int from the array + * @throws IndexOutOfBoundsException if the provided {@code offset} is outside + * the range [0, array.length - 4] + * @see #setUnsignedInt(byte[], int, long) + */ + @ForceInline + public static long getUnsignedInt(byte[] array, int offset) { + return Integer.toUnsignedLong(getInt(array, offset)); + } + /** * {@return a {@code float} from the provided {@code array} at the given {@code offset} * using little endian order}. @@ -308,6 +326,24 @@ public static void setInt(byte[] array, int offset, int value) { false); } + /** + * Sets (writes) the provided {@code value} using big endian order into + * the provided {@code array} beginning at the given {@code offset}. + *

+ * There are no access alignment requirements. + * + * @param array to set (write) a value into + * @param offset where setting (writing) in the array should begin + * @param value value to set in the array + * @throws IndexOutOfBoundsException if the provided {@code offset} is outside + * the range [0, array.length - 4] + * @see #getUnsignedInt(byte[], int) + */ + @ForceInline + public static void setUnsignedInt(byte[] array, int offset, long value) { + setInt(array, offset, (int) value); + } + /** * Sets (writes) the provided {@code value} using little endian order into * the provided {@code array} beginning at the given {@code offset}. From 9797ef4a1b1c0ffef0b687320cc5174967f797ca Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 14:58:38 +0800 Subject: [PATCH 03/12] delete extraneous whitespace --- src/java.base/share/classes/jdk/internal/util/ByteArray.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 789f8ebb6eed4..61a2d2e88b2b7 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -206,8 +206,8 @@ public static float getFloat(byte[] array, int offset) { @ForceInline public static long getLong(byte[] array, int offset) { return UNSAFE.getLongUnaligned( - array, - arrayOffset(array, Long.BYTES, offset), + array, + arrayOffset(array, Long.BYTES, offset), true); } From 340ad722bd68ce5e1221035c7407c96c17e7b1bc Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 15:07:37 +0800 Subject: [PATCH 04/12] delete extraneous whitespace --- src/java.base/share/classes/jdk/internal/util/ByteArray.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 61a2d2e88b2b7..3015af305951e 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -147,8 +147,8 @@ public static int getUnsignedShort(byte[] array, int offset) { @ForceInline public static int getInt(byte[] array, int offset) { return UNSAFE.getIntUnaligned( - array, - arrayOffset(array, Integer.BYTES, offset), + array, + arrayOffset(array, Integer.BYTES, offset), true); } From 6b14fa7e6e5a5ce78fe7a13613154e7c33a864bb Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 17:10:34 +0800 Subject: [PATCH 05/12] use Preconditions.AIOOBE_FORMATTER --- .../share/classes/jdk/internal/util/ByteArray.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 3015af305951e..220a4d10f644e 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -28,9 +28,6 @@ import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.ForceInline; -import java.util.List; -import java.util.function.BiFunction; - /** * Utility methods for packing/unpacking primitive values in/out of byte arrays * using {@linkplain java.nio.ByteOrder#BIG_ENDIAN big endian order} (aka. "network order"). @@ -43,14 +40,11 @@ public final class ByteArray { private ByteArray() { } - private static final BiFunction, ArrayIndexOutOfBoundsException> - OOBEF = Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new); - static final Unsafe UNSAFE = Unsafe.getUnsafe(); @ForceInline static long arrayOffset(byte[] array, int typeBytes, int offset) { - return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, OOBEF) + return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, Preconditions.AIOOBE_FORMATTER) + Unsafe.ARRAY_BYTE_BASE_OFFSET; } From 8a507b9a05fd0c3f7f65faab89fb47e092ddf457 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 17:24:06 +0800 Subject: [PATCH 06/12] update tests --- .../util/ByteArray/ReadWriteValues.java | 476 ++++++++++++++---- 1 file changed, 381 insertions(+), 95 deletions(-) diff --git a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java index 273aaf4fc8c70..76b6c32ef0607 100644 --- a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java +++ b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java @@ -30,6 +30,7 @@ */ import jdk.internal.util.ByteArray; +import jdk.internal.util.ByteArrayLittleEndian; import org.junit.jupiter.api.*; import java.util.concurrent.ThreadLocalRandom; @@ -37,6 +38,9 @@ import java.util.stream.LongStream; import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.ParameterizedTest; import static org.junit.jupiter.api.Assertions.*; final class ReadWriteValues { @@ -48,148 +52,197 @@ final class ReadWriteValues { private static final int ITERATIONS = 1 << 10; - @Test - void testGetShort() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetShort(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { short expected = (short) l; - RefImpl.putShort(BUFF, OFFSET, expected); - short actual = ByteArray.getShort(BUFF, OFFSET); + ref.setShort(BUFF, OFFSET, expected); + short actual = ba.getShort(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutShort() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetShort(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { short expected = (short) l; - ByteArray.setShort(BUFF, OFFSET, expected); - short actual = RefImpl.getShort(BUFF, OFFSET); + ba.setShort(BUFF, OFFSET, expected); + short actual = ref.getShort(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testGetChar() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetChar(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { char expected = (char) l; - RefImpl.putChar(BUFF, OFFSET, expected); - char actual = ByteArray.getChar(BUFF, OFFSET); + ref.setChar(BUFF, OFFSET, expected); + char actual = ba.getChar(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutChar() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetChar(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { char expected = (char) l; - ByteArray.setChar(BUFF, OFFSET, expected); - char actual = RefImpl.getChar(BUFF, OFFSET); + ba.setChar(BUFF, OFFSET, expected); + char actual = ref.getChar(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testGetInt() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetInt(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { int expected = (int) l; - RefImpl.putInt(BUFF, OFFSET, expected); - int actual = ByteArray.getInt(BUFF, OFFSET); + ref.setInt(BUFF, OFFSET, expected); + int actual = ba.getInt(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutInt() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetInt(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { int expected = (int) l; - ByteArray.setInt(BUFF, OFFSET, expected); - int actual = RefImpl.getInt(BUFF, OFFSET); + ba.setInt(BUFF, OFFSET, expected); + int actual = ref.getInt(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testGetLong() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetLong(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(expected -> { - RefImpl.putLong(BUFF, OFFSET, expected); - long actual = ByteArray.getLong(BUFF, OFFSET); + ref.setLong(BUFF, OFFSET, expected); + long actual = ba.getLong(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutLong() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetLong(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(expected -> { - ByteArray.setLong(BUFF, OFFSET, expected); - long actual = RefImpl.getLong(BUFF, OFFSET); + ba.setLong(BUFF, OFFSET, expected); + long actual = ref.getLong(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testGetFloat() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetFloat(ByteArrayImpl ref, ByteArrayImpl ba) { floats().forEach(expected -> { - RefImpl.putFloat(BUFF, OFFSET, expected); - float actual = ByteArray.getFloat(BUFF, OFFSET); + ref.setFloat(BUFF, OFFSET, expected); + float actual = ba.getFloat(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutFloat() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetFloat(ByteArrayImpl ref, ByteArrayImpl ba) { floats().forEach(expected -> { - ByteArray.setFloat(BUFF, OFFSET, expected); - float actual = RefImpl.getFloat(BUFF, OFFSET); + ba.setFloat(BUFF, OFFSET, expected); + float actual = ref.getFloat(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testGetDouble() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetDouble(ByteArrayImpl ref, ByteArrayImpl ba) { doubles().forEach(expected -> { - RefImpl.putDouble(BUFF, OFFSET, expected); - double actual = ByteArray.getDouble(BUFF, OFFSET); + ref.setDouble(BUFF, OFFSET, expected); + double actual = ba.getDouble(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutDouble() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetDouble(ByteArrayImpl ref, ByteArrayImpl ba) { doubles().forEach(expected -> { - ByteArray.setDouble(BUFF, OFFSET, expected); - double actual = RefImpl.getDouble(BUFF, OFFSET); + ba.setDouble(BUFF, OFFSET, expected); + double actual = ref.getDouble(BUFF, OFFSET); assertEquals(expected, actual); }); } - @Test - void testPutUnsignedShort() { + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetUnsignedShort(ByteArrayImpl ref, ByteArrayImpl ba) { longs().forEach(l -> { int expected = Short.toUnsignedInt((short) l); - ByteArray.setUnsignedShort(BUFF, OFFSET, expected); - int actual = Short.toUnsignedInt(RefImpl.getShort(BUFF, OFFSET)); + ref.setUnsignedShort(BUFF, OFFSET, expected); + int actual = ba.getUnsignedShort(BUFF, OFFSET); + assertEquals(expected, actual); + }); + } + + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetUnsignedShort(ByteArrayImpl ref, ByteArrayImpl ba) { + longs().forEach(l -> { + int expected = Short.toUnsignedInt((short) l); + ba.setUnsignedShort(BUFF, OFFSET, expected); + int actual = ref.getUnsignedShort(BUFF, OFFSET); + assertEquals(expected, actual); + }); + } + + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testGetUnsignedInt(ByteArrayImpl ref, ByteArrayImpl ba) { + longs().forEach(l -> { + long expected = Integer.toUnsignedLong((int) l); + ref.setUnsignedInt(BUFF, OFFSET, expected); + long actual = ba.getUnsignedInt(BUFF, OFFSET); + assertEquals(expected, actual); + }); + } + + @ParameterizedTest + @MethodSource("byteArrayImplWithRef") + void testSetUnsignedInt(ByteArrayImpl ref, ByteArrayImpl ba) { + longs().forEach(l -> { + long expected = Integer.toUnsignedLong((int) l); + ba.setUnsignedInt(BUFF, OFFSET, expected); + long actual = ref.getUnsignedInt(BUFF, OFFSET); assertEquals(expected, actual); }); } // Unusual cases - @Test - void testNullArray() { - assertThrowsOriginal(NullPointerException.class, () -> ByteArray.getInt(null, OFFSET)); - assertThrowsOriginal(NullPointerException.class, () -> ByteArray.setInt(null, OFFSET, 1)); + @ParameterizedTest + @MethodSource("byteArrayImpl") + void testNullArray(ByteArrayImpl ba) { + assertThrowsOriginal(NullPointerException.class, () -> ba.getInt(null, OFFSET)); + assertThrowsOriginal(NullPointerException.class, () -> ba.setInt(null, OFFSET, 1)); } - @Test - void testNegArg() { - assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.getInt(BUFF, -1)); - assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.setInt(BUFF, -1, 1)); + @ParameterizedTest + @MethodSource("byteArrayImpl") + void testNegArg(ByteArrayImpl ba) { + assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ba.getInt(BUFF, -1)); + assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ba.setInt(BUFF, -1, 1)); } - @Test - void testOutOfBounds() { - assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.getInt(BUFF, BUFF.length)); - assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.setInt(BUFF, BUFF.length, 1)); + @ParameterizedTest + @MethodSource("byteArrayImpl") + void testOutOfBounds(ByteArrayImpl ba) { + assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ba.getInt(BUFF, BUFF.length)); + assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ba.setInt(BUFF, BUFF.length, 1)); } static LongStream longs() { @@ -208,6 +261,7 @@ static DoubleStream doubles() { +0.0d) ); } + static Stream floats() { return Stream.concat( ThreadLocalRandom.current().doubles(ITERATIONS).mapToObj(d -> (float)d), @@ -221,6 +275,16 @@ static Stream floats() { ); } + static Stream byteArrayImpl() { + return Stream.of(UNSAFE, UNSAFE_LE); + } + + static Stream byteArrayImplWithRef() { + return Stream.of( + Arguments.of(REF, UNSAFE), + Arguments.of(REF_LE, UNSAFE_LE)); + } + @FunctionalInterface interface ThrowingRunnable { void run() throws Exception; @@ -242,34 +306,83 @@ void assertThrowsOriginal(Class type, } + private interface ByteArrayImpl { + char getChar(byte[] b, int off); + + short getShort(byte[] b, int off); + + default int getUnsignedShort(byte[] b, int off) { + return Short.toUnsignedInt(getShort(b, off)); + } + + int getInt(byte[] b, int off); + + default long getUnsignedInt(byte[] b, int off) { + return Integer.toUnsignedLong(getInt(b, off)); + } + + long getLong(byte[] b, int off); + + default float getFloat(byte[] b, int off) { + return Float.intBitsToFloat(getInt(b, off)); + } + + default double getDouble(byte[] b, int off) { + return Double.longBitsToDouble(getLong(b, off)); + } + + /* + * Methods for packing primitive values into byte arrays starting at given + * offsets. + */ + + void setChar(byte[] b, int off, char val); + + void setShort(byte[] b, int off, short val); + + default void setUnsignedShort(byte[] b, int off, int val) { + setShort(b, off, (short) val); + } + + void setInt(byte[] b, int off, int val); + + default void setUnsignedInt(byte[] b, int off, long val) { + setInt(b, off, (int) val); + } + + void setLong(byte[] b, int off, long val); + + default void setFloat(byte[] b, int off, float val) { + setInt(b, off, Float.floatToIntBits(val)); + } + + default void setDouble(byte[] b, int off, double val) { + setDouble(b, off, Double.doubleToLongBits(val)); + } + } + /** * Reference implementation from the old java.io.Bits implementation */ - private static final class RefImpl { - private RefImpl() {} - - static char getChar(byte[] b, int off) { + private static final ByteArrayImpl REF = new ByteArrayImpl() { + public char getChar(byte[] b, int off) { return (char) ((b[off + 1] & 0xFF) + (b[off] << 8)); } - static short getShort(byte[] b, int off) { + public short getShort(byte[] b, int off) { return (short) ((b[off + 1] & 0xFF) + (b[off] << 8)); } - static int getInt(byte[] b, int off) { + public int getInt(byte[] b, int off) { return ((b[off + 3] & 0xFF)) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + ((b[off]) << 24); } - static float getFloat(byte[] b, int off) { - return Float.intBitsToFloat(getInt(b, off)); - } - - static long getLong(byte[] b, int off) { + public long getLong(byte[] b, int off) { return ((b[off + 7] & 0xFFL)) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + @@ -280,37 +393,24 @@ static long getLong(byte[] b, int off) { (((long) b[off]) << 56); } - static double getDouble(byte[] b, int off) { - return Double.longBitsToDouble(getLong(b, off)); - } - - /* - * Methods for packing primitive values into byte arrays starting at given - * offsets. - */ - - static void putChar(byte[] b, int off, char val) { + public void setChar(byte[] b, int off, char val) { b[off + 1] = (byte) (val); b[off] = (byte) (val >>> 8); } - static void putShort(byte[] b, int off, short val) { + public void setShort(byte[] b, int off, short val) { b[off + 1] = (byte) (val); b[off] = (byte) (val >>> 8); } - static void putInt(byte[] b, int off, int val) { + public void setInt(byte[] b, int off, int val) { b[off + 3] = (byte) (val); b[off + 2] = (byte) (val >>> 8); b[off + 1] = (byte) (val >>> 16); b[off] = (byte) (val >>> 24); } - static void putFloat(byte[] b, int off, float val) { - putInt(b, off, Float.floatToIntBits(val)); - } - - static void putLong(byte[] b, int off, long val) { + public void setLong(byte[] b, int off, long val) { b[off + 7] = (byte) (val); b[off + 6] = (byte) (val >>> 8); b[off + 5] = (byte) (val >>> 16); @@ -320,9 +420,195 @@ static void putLong(byte[] b, int off, long val) { b[off + 1] = (byte) (val >>> 48); b[off] = (byte) (val >>> 56); } + }; - static void putDouble(byte[] b, int off, double val) { - putLong(b, off, Double.doubleToLongBits(val)); + private static final ByteArrayImpl REF_LE = new ByteArrayImpl() { + public char getChar(byte[] b, int off) { + return (char) ((b[off] & 0xFF) + + (b[off + 1] << 8)); } - } + + public short getShort(byte[] b, int off) { + return (short) ((b[off] & 0xFF) + + (b[off + 1] << 8)); + } + + public int getInt(byte[] b, int off) { + return ((b[off] & 0xFF)) + + ((b[off + 1] & 0xFF) << 8) + + ((b[off + 2] & 0xFF) << 16) + + ((b[off + 3]) << 24); + } + + public long getLong(byte[] b, int off) { + return ((b[off] & 0xFFL)) + + ((b[off + 1] & 0xFFL) << 8) + + ((b[off + 2] & 0xFFL) << 16) + + ((b[off + 3] & 0xFFL) << 24) + + ((b[off + 4] & 0xFFL) << 32) + + ((b[off + 5] & 0xFFL) << 40) + + ((b[off + 6] & 0xFFL) << 48) + + (((long) b[off + 7]) << 56); + } + + public void setChar(byte[] b, int off, char val) { + b[off] = (byte) (val); + b[off + 1] = (byte) (val >>> 8); + } + + public void setShort(byte[] b, int off, short val) { + b[off] = (byte) (val); + b[off + 1] = (byte) (val >>> 8); + } + + public void setInt(byte[] b, int off, int val) { + b[off] = (byte) (val); + b[off + 1] = (byte) (val >>> 8); + b[off + 2] = (byte) (val >>> 16); + b[off + 3] = (byte) (val >>> 24); + } + + public void setLong(byte[] b, int off, long val) { + b[off] = (byte) (val); + b[off + 1] = (byte) (val >>> 8); + b[off + 2] = (byte) (val >>> 16); + b[off + 3] = (byte) (val >>> 24); + b[off + 4] = (byte) (val >>> 32); + b[off + 5] = (byte) (val >>> 40); + b[off + 6] = (byte) (val >>> 48); + b[off + 7] = (byte) (val >>> 56); + } + }; + + private static final ByteArrayImpl UNSAFE = new ByteArrayImpl() { + public char getChar(byte[] b, int off) { + return ByteArray.getChar(b, off); + } + + public short getShort(byte[] b, int off) { + return ByteArray.getShort(b, off); + } + + public int getUnsignedShort(byte[] b, int off) { + return ByteArray.getUnsignedShort(b, off); + } + + public int getInt(byte[] b, int off) { + return ByteArray.getInt(b, off); + } + + public long getUnsignedInt(byte[] b, int off) { + return ByteArray.getUnsignedInt(b, off); + } + + public long getLong(byte[] b, int off) { + return ByteArray.getLong(b, off); + } + + public float getFloat(byte[] b, int off) { + return ByteArray.getFloat(b, off); + } + + public double getDouble(byte[] b, int off) { + return ByteArray.getDouble(b, off); + } + + public void setChar(byte[] b, int off, char val) { + ByteArray.setChar(b, off, val); + } + + public void setShort(byte[] b, int off, short val) { + ByteArray.setShort(b, off, val); + } + + public void setUnsignedShort(byte[] b, int off, int val) { + ByteArray.setUnsignedShort(b, off, val); + } + + public void setInt(byte[] b, int off, int val) { + ByteArray.setInt(b, off, val); + } + + public void setUnsignedInt(byte[] b, int off, long val) { + ByteArray.setUnsignedInt(b, off, val); + } + + public void setLong(byte[] b, int off, long val) { + ByteArray.setLong(b, off, val); + } + + public void setFloat(byte[] b, int off, float val) { + ByteArray.setFloat(b, off, val); + } + + public void setDouble(byte[] b, int off, double val) { + ByteArray.setDouble(b, off, val); + } + }; + + private static final ByteArrayImpl UNSAFE_LE = new ByteArrayImpl() { + public char getChar(byte[] b, int off) { + return ByteArrayLittleEndian.getChar(b, off); + } + + public short getShort(byte[] b, int off) { + return ByteArrayLittleEndian.getShort(b, off); + } + + public int getUnsignedShort(byte[] b, int off) { + return ByteArrayLittleEndian.getUnsignedShort(b, off); + } + + public int getInt(byte[] b, int off) { + return ByteArrayLittleEndian.getInt(b, off); + } + + public long getUnsignedInt(byte[] b, int off) { + return ByteArrayLittleEndian.getUnsignedInt(b, off); + } + + public long getLong(byte[] b, int off) { + return ByteArrayLittleEndian.getLong(b, off); + } + + public float getFloat(byte[] b, int off) { + return ByteArrayLittleEndian.getFloat(b, off); + } + + public double getDouble(byte[] b, int off) { + return ByteArrayLittleEndian.getDouble(b, off); + } + + public void setChar(byte[] b, int off, char val) { + ByteArrayLittleEndian.setChar(b, off, val); + } + + public void setShort(byte[] b, int off, short val) { + ByteArrayLittleEndian.setShort(b, off, val); + } + + public void setUnsignedShort(byte[] b, int off, int val) { + ByteArrayLittleEndian.setUnsignedShort(b, off, val); + } + + public void setInt(byte[] b, int off, int val) { + ByteArrayLittleEndian.setInt(b, off, val); + } + + public void setUnsignedInt(byte[] b, int off, long val) { + ByteArrayLittleEndian.setUnsignedInt(b, off, val); + } + + public void setLong(byte[] b, int off, long val) { + ByteArrayLittleEndian.setLong(b, off, val); + } + + public void setFloat(byte[] b, int off, float val) { + ByteArrayLittleEndian.setFloat(b, off, val); + } + + public void setDouble(byte[] b, int off, double val) { + ByteArrayLittleEndian.setDouble(b, off, val); + } + }; } From 20b7c875442e54cac97a14eb8d74c22ebc46efbe Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 18:07:50 +0800 Subject: [PATCH 07/12] fix test --- test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java index 76b6c32ef0607..3531a7256a5f8 100644 --- a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java +++ b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java @@ -357,7 +357,7 @@ default void setFloat(byte[] b, int off, float val) { } default void setDouble(byte[] b, int off, double val) { - setDouble(b, off, Double.doubleToLongBits(val)); + setLong(b, off, Double.doubleToLongBits(val)); } } From 8c3a2087b2005ff47c5ce87d92669c0dec1c685f Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 18:57:27 +0800 Subject: [PATCH 08/12] delete extraneous whitespace --- test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java index 3531a7256a5f8..9684378d2eac1 100644 --- a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java +++ b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java @@ -322,11 +322,11 @@ default long getUnsignedInt(byte[] b, int off) { } long getLong(byte[] b, int off); - + default float getFloat(byte[] b, int off) { return Float.intBitsToFloat(getInt(b, off)); } - + default double getDouble(byte[] b, int off) { return Double.longBitsToDouble(getLong(b, off)); } From e82690281530d7985ce8d0765b199a51a7caf177 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 19:54:12 +0800 Subject: [PATCH 09/12] add javadoc --- .../share/classes/jdk/internal/util/ByteArray.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 220a4d10f644e..b28fbaf582867 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -40,6 +40,14 @@ public final class ByteArray { private ByteArray() { } + /** + * The {@code Unsafe} can be functionality replaced by + * {@linkplain java.lang.invoke.MethodHandles#byteArrayViewVarHandle byteArrayViewVarHandle}, + * but it's not feasible in practices, because {@code ByteArray} and {@code ByteArrayLittleEndian} + * can be used in fundamental classes, {@code VarHandle} exercise many other + * code at VM startup, this could lead a recursive calls when fundamental + * classes is used in {@code VarHandle}. + */ static final Unsafe UNSAFE = Unsafe.getUnsafe(); @ForceInline From 4ac263254772c48546817c13212f6b36444c15f7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 24 Jun 2023 19:56:36 +0800 Subject: [PATCH 10/12] delete extraneous whitespace --- src/java.base/share/classes/jdk/internal/util/ByteArray.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index b28fbaf582867..df687ccef697e 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -41,9 +41,9 @@ private ByteArray() { } /** - * The {@code Unsafe} can be functionality replaced by + * The {@code Unsafe} can be functionality replaced by * {@linkplain java.lang.invoke.MethodHandles#byteArrayViewVarHandle byteArrayViewVarHandle}, - * but it's not feasible in practices, because {@code ByteArray} and {@code ByteArrayLittleEndian} + * but it's not feasible in practices, because {@code ByteArray} and {@code ByteArrayLittleEndian} * can be used in fundamental classes, {@code VarHandle} exercise many other * code at VM startup, this could lead a recursive calls when fundamental * classes is used in {@code VarHandle}. From 217caa3c8035a7646c3786f9852dcf798387e499 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 26 Jun 2023 14:57:36 +0800 Subject: [PATCH 11/12] delete incorrect comments --- .../share/classes/jdk/internal/util/ByteArray.java | 8 -------- .../classes/jdk/internal/util/ByteArrayLittleEndian.java | 8 -------- 2 files changed, 16 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index df687ccef697e..b95647c5085d5 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -176,8 +176,6 @@ public static long getUnsignedInt(byte[] array, int offset) { * {@return a {@code float} from the provided {@code array} at the given {@code offset} * using big endian order}. *

- * Variants of {@linkplain Float#NaN } values are canonized to a single NaN value. - *

* There are no access alignment requirements. * * @param array to get a value from. @@ -188,8 +186,6 @@ public static long getUnsignedInt(byte[] array, int offset) { */ @ForceInline public static float getFloat(byte[] array, int offset) { - // Using Float.intBitsToFloat collapses NaN values to a single - // "canonical" NaN value return Float.intBitsToFloat(getInt(array, offset)); } @@ -217,8 +213,6 @@ public static long getLong(byte[] array, int offset) { * {@return a {@code double} from the provided {@code array} at the given {@code offset} * using big endian order}. *

- * Variants of {@linkplain Double#NaN } values are canonized to a single NaN value. - *

* There are no access alignment requirements. * * @param array to get a value from. @@ -229,8 +223,6 @@ public static long getLong(byte[] array, int offset) { */ @ForceInline public static double getDouble(byte[] array, int offset) { - // Using Double.longBitsToDouble collapses NaN values to a single - // "canonical" NaN value return Double.longBitsToDouble(getLong(array, offset)); } diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index 736b2bb879091..11ceae46a2af0 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java @@ -163,8 +163,6 @@ public static long getUnsignedInt(byte[] array, int offset) { * {@return a {@code float} from the provided {@code array} at the given {@code offset} * using little endian order}. *

- * Variants of {@linkplain Float#NaN } values are canonized to a single NaN value. - *

* There are no access alignment requirements. * * @param array to get a value from. @@ -175,8 +173,6 @@ public static long getUnsignedInt(byte[] array, int offset) { */ @ForceInline public static float getFloat(byte[] array, int offset) { - // Using Float.intBitsToFloat collapses NaN values to a single - // "canonical" NaN value return Float.intBitsToFloat(getInt(array, offset)); } @@ -204,8 +200,6 @@ public static long getLong(byte[] array, int offset) { * {@return a {@code double} from the provided {@code array} at the given {@code offset} * using little endian order}. *

- * Variants of {@linkplain Double#NaN } values are canonized to a single NaN value. - *

* There are no access alignment requirements. * * @param array to get a value from. @@ -216,8 +210,6 @@ public static long getLong(byte[] array, int offset) { */ @ForceInline public static double getDouble(byte[] array, int offset) { - // Using Double.longBitsToDouble collapses NaN values to a single - // "canonical" NaN value return Double.longBitsToDouble(getLong(array, offset)); } From 7cfef770ee696d0f2e5507c4e5cea69bd78a897a Mon Sep 17 00:00:00 2001 From: Glavo Date: Wed, 28 Jun 2023 04:23:53 +0800 Subject: [PATCH 12/12] add 8310843 to @bug --- test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java index 9684378d2eac1..138c1c1842021 100644 --- a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java +++ b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8299576 + * @bug 8299576 8310843 * @modules java.base/jdk.internal.util * @summary Verify that reads and writes of primitives are correct * @run junit ReadWriteValues