| /* | |
| * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package jdk.internal.util; | |
| import java.lang.invoke.MethodHandles; | |
| import java.lang.invoke.VarHandle; | |
| import java.nio.ByteOrder; | |
| /** | |
| * Utility methods for packing/unpacking primitive values in/out of byte arrays | |
| * using {@linkplain ByteOrder#LITTLE_ENDIAN little endian order}. | |
| * <p> | |
| * All methods in this class will throw an {@linkplain NullPointerException} if {@code null} is | |
| * passed in as a method parameter for a byte array. | |
| */ | |
| 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. | |
| */ | |
| /** | |
| * {@return a {@code boolean} from the provided {@code array} at the given {@code offset}}. | |
| * | |
| * @param array to read 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 - 1] | |
| * @see #setBoolean(byte[], int, boolean) | |
| */ | |
| public static boolean getBoolean(byte[] array, int offset) { | |
| return array[offset] != 0; | |
| } | |
| /** | |
| * {@return a {@code char} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * 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 - 2] | |
| * @see #setChar(byte[], int, char) | |
| */ | |
| public static char getChar(byte[] array, int offset) { | |
| return (char) CHAR.get(array, offset); | |
| } | |
| /** | |
| * {@return a {@code short} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * There are no access alignment requirements. | |
| * | |
| * @param array to get a value from. | |
| * @param offset where extraction in the array should begin | |
| * @return a {@code short} from the array | |
| * @throws IndexOutOfBoundsException if the provided {@code offset} is outside | |
| * the range [0, array.length - 2] | |
| * @see #setShort(byte[], int, short) | |
| */ | |
| public static short getShort(byte[] array, int offset) { | |
| return (short) SHORT.get(array, offset); | |
| } | |
| /** | |
| * {@return an {@code unsigned short} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * 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 int} representing an unsigned short from the array | |
| * @throws IndexOutOfBoundsException if the provided {@code offset} is outside | |
| * the range [0, array.length - 2] | |
| * @see #setUnsignedShort(byte[], int, int) | |
| */ | |
| public static int getUnsignedShort(byte[] array, int offset) { | |
| return Short.toUnsignedInt((short) SHORT.get(array, offset)); | |
| } | |
| /** | |
| * {@return an {@code int} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * 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 #setInt(byte[], int, int) | |
| */ | |
| public static int getInt(byte[] array, int offset) { | |
| return (int) INT.get(array, offset); | |
| } | |
| /** | |
| * {@return a {@code float} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * Variants of {@linkplain Float#NaN } values are canonized to a single NaN value. | |
| * <p> | |
| * 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 #setFloat(byte[], int, float) | |
| */ | |
| 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}. | |
| * <p> | |
| * Variants of {@linkplain Float#NaN } values are silently read according | |
| * to their bit patterns. | |
| * <p> | |
| * 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 a {@code long} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * 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 #setLong(byte[], int, long) | |
| */ | |
| public static long getLong(byte[] array, int offset) { | |
| return (long) LONG.get(array, offset); | |
| } | |
| /** | |
| * {@return a {@code double} from the provided {@code array} at the given {@code offset} | |
| * using little endian order}. | |
| * <p> | |
| * Variants of {@linkplain Double#NaN } values are canonized to a single NaN value. | |
| * <p> | |
| * 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 #setDouble(byte[], int, double) | |
| */ | |
| 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}. | |
| * <p> | |
| * Variants of {@linkplain Double#NaN } values are silently read according to | |
| * their bit patterns. | |
| * <p> | |
| * 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); | |
| } | |
| /* | |
| * Methods for packing primitive values into byte arrays starting at a given | |
| * offset. | |
| */ | |
| /** | |
| * Sets (writes) the provided {@code value} into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * | |
| * @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] | |
| * @see #getBoolean(byte[], int) | |
| */ | |
| public static void setBoolean(byte[] array, int offset, boolean value) { | |
| array[offset] = (byte) (value ? 1 : 0); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getChar(byte[], int) | |
| */ | |
| public static void setChar(byte[] array, int offset, char value) { | |
| CHAR.set(array, offset, value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getShort(byte[], int) | |
| */ | |
| public static void setShort(byte[] array, int offset, short value) { | |
| SHORT.set(array, offset, value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getUnsignedShort(byte[], int) | |
| */ | |
| public static void setUnsignedShort(byte[] array, int offset, int value) { | |
| SHORT.set(array, offset, (short) (char) value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * 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 #getInt(byte[], int) | |
| */ | |
| public static void setInt(byte[] array, int offset, int value) { | |
| INT.set(array, offset, value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * Variants of {@linkplain Float#NaN } values are canonized to a single NaN value. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getFloat(byte[], int) | |
| */ | |
| 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)); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * Variants of {@linkplain Float#NaN } values are silently written according to | |
| * their bit patterns. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getFloatRaw(byte[], int) | |
| */ | |
| public static void setFloatRaw(byte[] array, int offset, float value) { | |
| // Just sets the bits as they are | |
| FLOAT.set(array, offset, value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * 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 #getLong(byte[], int) | |
| */ | |
| public static void setLong(byte[] array, int offset, long value) { | |
| LONG.set(array, offset, value); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * Variants of {@linkplain Double#NaN } values are canonized to a single NaN value. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getDouble(byte[], int) | |
| */ | |
| 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)); | |
| } | |
| /** | |
| * Sets (writes) the provided {@code value} using little endian order into | |
| * the provided {@code array} beginning at the given {@code offset}. | |
| * <p> | |
| * Variants of {@linkplain Double#NaN } values are silently written according to | |
| * their bit patterns. | |
| * <p> | |
| * 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 - 2] | |
| * @see #getDoubleRaw(byte[], int) | |
| */ | |
| 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); | |
| } | |
| } |