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..5c54a1a9b4556 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -25,13 +25,12 @@ 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; /** * 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 +40,21 @@ 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); + /** + * 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 + static long arrayOffset(byte[] array, int typeBytes, int offset) { + return (long) Preconditions.checkIndex(offset, array.length - typeBytes + 1, Preconditions.AIOOBE_FORMATTER) + + Unsafe.ARRAY_BYTE_BASE_OFFSET; + } /* * Methods for unpacking primitive values from byte arrays starting at @@ -62,6 +70,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 +87,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 +108,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 +129,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,48 +146,47 @@ 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); } /** - * {@return a {@code float} from the provided {@code array} at the given {@code offset} + * {@return an {@code unsigned int} 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. * @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 #setFloat(byte[], int, float) + * @see #setUnsignedInt(byte[], int, long) */ - 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)); + @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}. *
- * 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) + * @see #setFloat(byte[], int, float) */ - public static float getFloatRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (float) FLOAT.get(array, offset); + @ForceInline + public static float getFloat(byte[] array, int offset) { + return Float.intBitsToFloat(getInt(array, offset)); } /** @@ -184,16 +201,18 @@ 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); } /** * {@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. @@ -202,30 +221,9 @@ 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 +242,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 +260,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 +282,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 +304,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 +322,31 @@ 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); + } + + /** + * 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); } /** @@ -331,10 +364,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 +385,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 +406,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 +430,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 +451,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..b150b8fa2f33d 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -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,48 +133,47 @@ 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); } - /** - * {@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. + /** + * {@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 #setFloat(byte[], int, float) + * @see #setUnsignedInt(byte[], int, long) */ - 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)); + @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}. *
- * 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) + * @see #setFloat(byte[], int, float) */ - public static float getFloatRaw(byte[] array, int offset) { - // Just gets the bits as they are - return (float) FLOAT.get(array, offset); + @ForceInline + public static float getFloat(byte[] array, int offset) { + return Float.intBitsToFloat(getInt(array, offset)); } /** @@ -184,16 +188,18 @@ 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); } /** * {@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. @@ -202,30 +208,9 @@ 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 +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, + false); } /** @@ -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, + false); } /** @@ -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,31 @@ 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); + } + + /** + * 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);
}
/**
@@ -331,10 +351,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 +372,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 +393,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 +417,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 +438,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));
}
}
diff --git a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java
index 273aaf4fc8c70..28222a4630fb9 100644
--- a/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java
+++ b/test/jdk/jdk/internal/util/ByteArray/ReadWriteValues.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024, 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
@@ -23,13 +23,14 @@
/*
* @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
*/
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