8300236: Use VarHandle access in Data(Input | Output)Stream classes

Reviewed-by: rriggs, alanb
This commit is contained in:
Per Minborg 2023-01-25 12:54:27 +00:00
parent a5d8e12872
commit 74e1a8bfa8
11 changed files with 869 additions and 364 deletions

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2001, 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 java.io;
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 big-endian byte ordering (i.e. "Network Order").
*/
final class Bits {
private Bits() {}
private static final VarHandle SHORT = create(short[].class);
private static final VarHandle INT = create(int[].class);
private static final VarHandle LONG = create(long[].class);
/*
* Methods for unpacking primitive values from byte arrays starting at
* given offsets.
*/
static boolean getBoolean(byte[] b, int off) {
return b[off] != 0;
}
static char getChar(byte[] b, int off) {
return (char) (short) SHORT.get(b, off);
}
static short getShort(byte[] b, int off) {
return (short) SHORT.get(b, off);
}
static int getInt(byte[] b, int off) {
return (int) INT.get(b, off);
}
static float getFloat(byte[] b, int off) {
// Using Float.intBitsToFloat collapses NaN values to a single
// "canonical" NaN value
return Float.intBitsToFloat((int) INT.get(b, off));
}
static long getLong(byte[] b, int off) {
return (long) LONG.get(b, off);
}
static double getDouble(byte[] b, int off) {
// Using Double.longBitsToDouble collapses NaN values to a single
// "canonical" NaN value
return Double.longBitsToDouble((long) LONG.get(b, off));
}
/*
* Methods for packing primitive values into byte arrays starting at given
* offsets.
*/
static void putBoolean(byte[] b, int off, boolean val) {
b[off] = (byte) (val ? 1 : 0);
}
static void putChar(byte[] b, int off, char val) {
SHORT.set(b, off, (short) val);
}
static void putShort(byte[] b, int off, short val) {
SHORT.set(b, off, val);
}
static void putInt(byte[] b, int off, int val) {
INT.set(b, off, val);
}
static void putFloat(byte[] b, int off, float val) {
// Using Float.floatToIntBits collapses NaN values to a single
// "canonical" NaN value
INT.set(b, off, Float.floatToIntBits(val));
}
static void putLong(byte[] b, int off, long val) {
LONG.set(b, off, val);
}
static void putDouble(byte[] b, int off, double val) {
// Using Double.doubleToLongBits collapses NaN values to a single
// "canonical" NaN value
LONG.set(b, off, Double.doubleToLongBits(val));
}
private static VarHandle create(Class<?> viewArrayClass) {
return MethodHandles.byteArrayViewVarHandle(viewArrayClass, ByteOrder.BIG_ENDIAN);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 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
@ -25,6 +25,8 @@
package java.io;
import jdk.internal.util.ByteArray;
import java.util.Objects;
/**
@ -54,6 +56,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
super(in);
}
private final byte[] readBuffer = new byte[8];
/**
* working arrays initialized on demand by readUTF
*/
@ -309,7 +313,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.io.FilterInputStream#in
*/
public final short readShort() throws IOException {
return (short) readUnsignedShort();
readFully(readBuffer, 0, 2);
return ByteArray.getShort(readBuffer, 0);
}
/**
@ -330,12 +335,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.io.FilterInputStream#in
*/
public final int readUnsignedShort() throws IOException {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (ch1 << 8) + (ch2 << 0);
readFully(readBuffer, 0, 2);
return ByteArray.getUnsignedShort(readBuffer, 0);
}
/**
@ -356,7 +357,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.io.FilterInputStream#in
*/
public final char readChar() throws IOException {
return (char) readUnsignedShort();
readFully(readBuffer, 0, 2);
return ByteArray.getChar(readBuffer, 0);
}
/**
@ -377,18 +379,10 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.io.FilterInputStream#in
*/
public final int readInt() throws IOException {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
readFully(readBuffer, 0, 4);
return ByteArray.getInt(readBuffer, 0);
}
private final byte[] readBuffer = new byte[8];
/**
* See the general contract of the {@code readLong}
* method of {@code DataInput}.
@ -408,14 +402,7 @@ public class DataInputStream extends FilterInputStream implements DataInput {
*/
public final long readLong() throws IOException {
readFully(readBuffer, 0, 8);
return (((long)readBuffer[0] << 56) +
((long)(readBuffer[1] & 255) << 48) +
((long)(readBuffer[2] & 255) << 40) +
((long)(readBuffer[3] & 255) << 32) +
((long)(readBuffer[4] & 255) << 24) +
((readBuffer[5] & 255) << 16) +
((readBuffer[6] & 255) << 8) +
((readBuffer[7] & 255) << 0));
return ByteArray.getLong(readBuffer, 0);
}
/**
@ -437,7 +424,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.lang.Float#intBitsToFloat(int)
*/
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
readFully(readBuffer, 0, 4);
return ByteArray.getFloat(readBuffer, 0);
}
/**
@ -459,7 +447,8 @@ public class DataInputStream extends FilterInputStream implements DataInput {
* @see java.lang.Double#longBitsToDouble(long)
*/
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
readFully(readBuffer, 0, 8);
return ByteArray.getDouble(readBuffer, 0);
}
private char[] lineBuffer;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 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
@ -25,6 +25,8 @@
package java.io;
import jdk.internal.util.ByteArray;
/**
* A data output stream lets an application write primitive Java data
* types to an output stream in a portable way. An application can
@ -170,8 +172,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.io.FilterOutputStream#out
*/
public final void writeShort(int v) throws IOException {
writeBuffer[0] = (byte)(v >>> 8);
writeBuffer[1] = (byte)(v >>> 0);
ByteArray.setUnsignedShort(writeBuffer, 0, v);
out.write(writeBuffer, 0, 2);
incCount(2);
}
@ -186,8 +187,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.io.FilterOutputStream#out
*/
public final void writeChar(int v) throws IOException {
writeBuffer[0] = (byte)(v >>> 8);
writeBuffer[1] = (byte)(v >>> 0);
ByteArray.setUnsignedShort(writeBuffer, 0, v);
out.write(writeBuffer, 0, 2);
incCount(2);
}
@ -202,10 +202,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.io.FilterOutputStream#out
*/
public final void writeInt(int v) throws IOException {
writeBuffer[0] = (byte)(v >>> 24);
writeBuffer[1] = (byte)(v >>> 16);
writeBuffer[2] = (byte)(v >>> 8);
writeBuffer[3] = (byte)(v >>> 0);
ByteArray.setInt(writeBuffer, 0, v);
out.write(writeBuffer, 0, 4);
incCount(4);
}
@ -220,14 +217,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.io.FilterOutputStream#out
*/
public final void writeLong(long v) throws IOException {
writeBuffer[0] = (byte)(v >>> 56);
writeBuffer[1] = (byte)(v >>> 48);
writeBuffer[2] = (byte)(v >>> 40);
writeBuffer[3] = (byte)(v >>> 32);
writeBuffer[4] = (byte)(v >>> 24);
writeBuffer[5] = (byte)(v >>> 16);
writeBuffer[6] = (byte)(v >>> 8);
writeBuffer[7] = (byte)(v >>> 0);
ByteArray.setLong(writeBuffer, 0, v);
out.write(writeBuffer, 0, 8);
incCount(8);
}
@ -246,7 +236,9 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.lang.Float#floatToIntBits(float)
*/
public final void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
ByteArray.setFloat(writeBuffer, 0, v);
out.write(writeBuffer, 0, 4);
incCount(4);
}
/**
@ -263,7 +255,9 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
* @see java.lang.Double#doubleToLongBits(double)
*/
public final void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
ByteArray.setDouble(writeBuffer, 0, v);
out.write(writeBuffer, 0, 8);
incCount(8);
}
/**
@ -301,8 +295,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
int len = s.length();
for (int i = 0 ; i < len ; i++) {
int v = s.charAt(i);
writeBuffer[0] = (byte)(v >>> 8);
writeBuffer[1] = (byte)(v >>> 0);
ByteArray.setUnsignedShort(writeBuffer, 0, v);
out.write(writeBuffer, 0, 2);
}
incCount(len * 2);
@ -379,9 +372,8 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
}
int count = 0;
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
ByteArray.setUnsignedShort(bytearr, count, utflen);
count += 2;
int i = 0;
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
int c = str.charAt(i);

View File

@ -45,6 +45,7 @@ import java.util.Objects;
import jdk.internal.access.SharedSecrets;
import jdk.internal.event.DeserializationEvent;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ByteArray;
import sun.reflect.misc.ReflectUtil;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetIntegerAction;
@ -2631,7 +2632,7 @@ public class ObjectInputStream
public boolean get(String name, boolean val) {
int off = getFieldOffset(name, Boolean.TYPE);
return (off >= 0) ? Bits.getBoolean(primValues, off) : val;
return (off >= 0) ? ByteArray.getBoolean(primValues, off) : val;
}
public byte get(String name, byte val) {
@ -2641,32 +2642,32 @@ public class ObjectInputStream
public char get(String name, char val) {
int off = getFieldOffset(name, Character.TYPE);
return (off >= 0) ? Bits.getChar(primValues, off) : val;
return (off >= 0) ? ByteArray.getChar(primValues, off) : val;
}
public short get(String name, short val) {
int off = getFieldOffset(name, Short.TYPE);
return (off >= 0) ? Bits.getShort(primValues, off) : val;
return (off >= 0) ? ByteArray.getShort(primValues, off) : val;
}
public int get(String name, int val) {
int off = getFieldOffset(name, Integer.TYPE);
return (off >= 0) ? Bits.getInt(primValues, off) : val;
return (off >= 0) ? ByteArray.getInt(primValues, off) : val;
}
public float get(String name, float val) {
int off = getFieldOffset(name, Float.TYPE);
return (off >= 0) ? Bits.getFloat(primValues, off) : val;
return (off >= 0) ? ByteArray.getFloat(primValues, off) : val;
}
public long get(String name, long val) {
int off = getFieldOffset(name, Long.TYPE);
return (off >= 0) ? Bits.getLong(primValues, off) : val;
return (off >= 0) ? ByteArray.getLong(primValues, off) : val;
}
public double get(String name, double val) {
int off = getFieldOffset(name, Double.TYPE);
return (off >= 0) ? Bits.getDouble(primValues, off) : val;
return (off >= 0) ? ByteArray.getDouble(primValues, off) : val;
}
public Object get(String name, Object val) throws ClassNotFoundException {
@ -3114,7 +3115,7 @@ public class ObjectInputStream
return HEADER_BLOCKED;
}
in.readFully(hbuf, 0, 5);
int len = Bits.getInt(hbuf, 1);
int len = ByteArray.getInt(hbuf, 1);
if (len < 0) {
throw new StreamCorruptedException(
"illegal block data header length: " +
@ -3413,7 +3414,7 @@ public class ObjectInputStream
} else if (end - pos < 2) {
return din.readChar();
}
char v = Bits.getChar(buf, pos);
char v = ByteArray.getChar(buf, pos);
pos += 2;
return v;
}
@ -3425,7 +3426,7 @@ public class ObjectInputStream
} else if (end - pos < 2) {
return din.readShort();
}
short v = Bits.getShort(buf, pos);
short v = ByteArray.getShort(buf, pos);
pos += 2;
return v;
}
@ -3437,7 +3438,7 @@ public class ObjectInputStream
} else if (end - pos < 2) {
return din.readUnsignedShort();
}
int v = Bits.getShort(buf, pos) & 0xFFFF;
int v = ByteArray.getShort(buf, pos) & 0xFFFF;
pos += 2;
return v;
}
@ -3449,7 +3450,7 @@ public class ObjectInputStream
} else if (end - pos < 4) {
return din.readInt();
}
int v = Bits.getInt(buf, pos);
int v = ByteArray.getInt(buf, pos);
pos += 4;
return v;
}
@ -3461,7 +3462,7 @@ public class ObjectInputStream
} else if (end - pos < 4) {
return din.readFloat();
}
float v = Bits.getFloat(buf, pos);
float v = ByteArray.getFloat(buf, pos);
pos += 4;
return v;
}
@ -3473,7 +3474,7 @@ public class ObjectInputStream
} else if (end - pos < 8) {
return din.readLong();
}
long v = Bits.getLong(buf, pos);
long v = ByteArray.getLong(buf, pos);
pos += 8;
return v;
}
@ -3485,7 +3486,7 @@ public class ObjectInputStream
} else if (end - pos < 8) {
return din.readDouble();
}
double v = Bits.getDouble(buf, pos);
double v = ByteArray.getDouble(buf, pos);
pos += 8;
return v;
}
@ -3523,7 +3524,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getBoolean(buf, pos++);
v[off++] = ByteArray.getBoolean(buf, pos++);
}
}
}
@ -3544,7 +3545,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getChar(buf, pos);
v[off++] = ByteArray.getChar(buf, pos);
pos += 2;
}
}
@ -3566,7 +3567,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getShort(buf, pos);
v[off++] = ByteArray.getShort(buf, pos);
pos += 2;
}
}
@ -3588,7 +3589,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getInt(buf, pos);
v[off++] = ByteArray.getInt(buf, pos);
pos += 4;
}
}
@ -3610,7 +3611,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getFloat(buf, pos);
v[off++] = ByteArray.getFloat(buf, pos);
pos += 4;
}
}
@ -3632,7 +3633,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getLong(buf, pos);
v[off++] = ByteArray.getLong(buf, pos);
pos += 8;
}
}
@ -3654,7 +3655,7 @@ public class ObjectInputStream
}
while (off < stop) {
v[off++] = Bits.getDouble(buf, pos);
v[off++] = ByteArray.getDouble(buf, pos);
pos += 8;
}
}

View File

@ -32,6 +32,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import jdk.internal.util.ByteArray;
import sun.reflect.misc.ReflectUtil;
/**
@ -1638,7 +1640,7 @@ public class ObjectOutputStream
}
public void put(String name, boolean val) {
Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
ByteArray.setBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
}
public void put(String name, byte val) {
@ -1646,27 +1648,27 @@ public class ObjectOutputStream
}
public void put(String name, char val) {
Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
ByteArray.setChar(primVals, getFieldOffset(name, Character.TYPE), val);
}
public void put(String name, short val) {
Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
ByteArray.setShort(primVals, getFieldOffset(name, Short.TYPE), val);
}
public void put(String name, int val) {
Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
ByteArray.setInt(primVals, getFieldOffset(name, Integer.TYPE), val);
}
public void put(String name, float val) {
Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
ByteArray.setFloat(primVals, getFieldOffset(name, Float.TYPE), val);
}
public void put(String name, long val) {
Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
ByteArray.setLong(primVals, getFieldOffset(name, Long.TYPE), val);
}
public void put(String name, double val) {
Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
ByteArray.setDouble(primVals, getFieldOffset(name, Double.TYPE), val);
}
public void put(String name, Object val) {
@ -1908,7 +1910,7 @@ public class ObjectOutputStream
out.write(hbuf, 0, 2);
} else {
hbuf[0] = TC_BLOCKDATALONG;
Bits.putInt(hbuf, 1, len);
ByteArray.setInt(hbuf, 1, len);
out.write(hbuf, 0, 5);
}
}
@ -1925,7 +1927,7 @@ public class ObjectOutputStream
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
Bits.putBoolean(buf, pos++, v);
ByteArray.setBoolean(buf, pos++, v);
}
public void writeByte(int v) throws IOException {
@ -1937,7 +1939,7 @@ public class ObjectOutputStream
public void writeChar(int v) throws IOException {
if (pos + 2 <= MAX_BLOCK_SIZE) {
Bits.putChar(buf, pos, (char) v);
ByteArray.setChar(buf, pos, (char) v);
pos += 2;
} else {
dout.writeChar(v);
@ -1946,7 +1948,7 @@ public class ObjectOutputStream
public void writeShort(int v) throws IOException {
if (pos + 2 <= MAX_BLOCK_SIZE) {
Bits.putShort(buf, pos, (short) v);
ByteArray.setShort(buf, pos, (short) v);
pos += 2;
} else {
dout.writeShort(v);
@ -1955,7 +1957,7 @@ public class ObjectOutputStream
public void writeInt(int v) throws IOException {
if (pos + 4 <= MAX_BLOCK_SIZE) {
Bits.putInt(buf, pos, v);
ByteArray.setInt(buf, pos, v);
pos += 4;
} else {
dout.writeInt(v);
@ -1964,7 +1966,7 @@ public class ObjectOutputStream
public void writeFloat(float v) throws IOException {
if (pos + 4 <= MAX_BLOCK_SIZE) {
Bits.putFloat(buf, pos, v);
ByteArray.setFloat(buf, pos, v);
pos += 4;
} else {
dout.writeFloat(v);
@ -1973,7 +1975,7 @@ public class ObjectOutputStream
public void writeLong(long v) throws IOException {
if (pos + 8 <= MAX_BLOCK_SIZE) {
Bits.putLong(buf, pos, v);
ByteArray.setLong(buf, pos, v);
pos += 8;
} else {
dout.writeLong(v);
@ -1982,7 +1984,7 @@ public class ObjectOutputStream
public void writeDouble(double v) throws IOException {
if (pos + 8 <= MAX_BLOCK_SIZE) {
Bits.putDouble(buf, pos, v);
ByteArray.setDouble(buf, pos, v);
pos += 8;
} else {
dout.writeDouble(v);
@ -2042,7 +2044,7 @@ public class ObjectOutputStream
}
int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
while (off < stop) {
Bits.putBoolean(buf, pos++, v[off++]);
ByteArray.setBoolean(buf, pos++, v[off++]);
}
}
}
@ -2055,7 +2057,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 1;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putChar(buf, pos, v[off++]);
ByteArray.setChar(buf, pos, v[off++]);
pos += 2;
}
} else {
@ -2072,7 +2074,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 1;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putShort(buf, pos, v[off++]);
ByteArray.setShort(buf, pos, v[off++]);
pos += 2;
}
} else {
@ -2089,7 +2091,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 2;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putInt(buf, pos, v[off++]);
ByteArray.setInt(buf, pos, v[off++]);
pos += 4;
}
} else {
@ -2106,7 +2108,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 2;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putFloat(buf, pos, v[off++]);
ByteArray.setFloat(buf, pos, v[off++]);
pos += 4;
}
} else {
@ -2123,7 +2125,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 3;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putLong(buf, pos, v[off++]);
ByteArray.setLong(buf, pos, v[off++]);
pos += 8;
}
} else {
@ -2140,7 +2142,7 @@ public class ObjectOutputStream
int avail = (MAX_BLOCK_SIZE - pos) >> 3;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putDouble(buf, pos, v[off++]);
ByteArray.setDouble(buf, pos, v[off++]);
pos += 8;
}
} else {

View File

@ -61,6 +61,7 @@ import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaSecurityAccess;
import jdk.internal.util.ByteArray;
import sun.reflect.misc.ReflectUtil;
/**
@ -1986,14 +1987,14 @@ public final class ObjectStreamClass implements Serializable {
long key = readKeys[i];
int off = offsets[i];
switch (typeCodes[i]) {
case 'Z' -> Bits.putBoolean(buf, off, UNSAFE.getBoolean(obj, key));
case 'Z' -> ByteArray.setBoolean(buf, off, UNSAFE.getBoolean(obj, key));
case 'B' -> buf[off] = UNSAFE.getByte(obj, key);
case 'C' -> Bits.putChar(buf, off, UNSAFE.getChar(obj, key));
case 'S' -> Bits.putShort(buf, off, UNSAFE.getShort(obj, key));
case 'I' -> Bits.putInt(buf, off, UNSAFE.getInt(obj, key));
case 'F' -> Bits.putFloat(buf, off, UNSAFE.getFloat(obj, key));
case 'J' -> Bits.putLong(buf, off, UNSAFE.getLong(obj, key));
case 'D' -> Bits.putDouble(buf, off, UNSAFE.getDouble(obj, key));
case 'C' -> ByteArray.setChar(buf, off, UNSAFE.getChar(obj, key));
case 'S' -> ByteArray.setShort(buf, off, UNSAFE.getShort(obj, key));
case 'I' -> ByteArray.setInt(buf, off, UNSAFE.getInt(obj, key));
case 'F' -> ByteArray.setFloat(buf, off, UNSAFE.getFloat(obj, key));
case 'J' -> ByteArray.setLong(buf, off, UNSAFE.getLong(obj, key));
case 'D' -> ByteArray.setDouble(buf, off, UNSAFE.getDouble(obj, key));
default -> throw new InternalError();
}
}
@ -2015,14 +2016,14 @@ public final class ObjectStreamClass implements Serializable {
}
int off = offsets[i];
switch (typeCodes[i]) {
case 'Z' -> UNSAFE.putBoolean(obj, key, Bits.getBoolean(buf, off));
case 'Z' -> UNSAFE.putBoolean(obj, key, ByteArray.getBoolean(buf, off));
case 'B' -> UNSAFE.putByte(obj, key, buf[off]);
case 'C' -> UNSAFE.putChar(obj, key, Bits.getChar(buf, off));
case 'S' -> UNSAFE.putShort(obj, key, Bits.getShort(buf, off));
case 'I' -> UNSAFE.putInt(obj, key, Bits.getInt(buf, off));
case 'F' -> UNSAFE.putFloat(obj, key, Bits.getFloat(buf, off));
case 'J' -> UNSAFE.putLong(obj, key, Bits.getLong(buf, off));
case 'D' -> UNSAFE.putDouble(obj, key, Bits.getDouble(buf, off));
case 'C' -> UNSAFE.putChar(obj, key, ByteArray.getChar(buf, off));
case 'S' -> UNSAFE.putShort(obj, key, ByteArray.getShort(buf, off));
case 'I' -> UNSAFE.putInt(obj, key, ByteArray.getInt(buf, off));
case 'F' -> UNSAFE.putFloat(obj, key, ByteArray.getFloat(buf, off));
case 'J' -> UNSAFE.putLong(obj, key, ByteArray.getLong(buf, off));
case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
default -> throw new InternalError();
}
}
@ -2473,16 +2474,16 @@ public final class ObjectStreamClass implements Serializable {
try {
PRIM_VALUE_EXTRACTORS = Map.of(
byte.class, MethodHandles.arrayElementGetter(byte[].class),
short.class, lkp.findStatic(Bits.class, "getShort", MethodType.methodType(short.class, byte[].class, int.class)),
int.class, lkp.findStatic(Bits.class, "getInt", MethodType.methodType(int.class, byte[].class, int.class)),
long.class, lkp.findStatic(Bits.class, "getLong", MethodType.methodType(long.class, byte[].class, int.class)),
float.class, lkp.findStatic(Bits.class, "getFloat", MethodType.methodType(float.class, byte[].class, int.class)),
double.class, lkp.findStatic(Bits.class, "getDouble", MethodType.methodType(double.class, byte[].class, int.class)),
char.class, lkp.findStatic(Bits.class, "getChar", MethodType.methodType(char.class, byte[].class, int.class)),
boolean.class, lkp.findStatic(Bits.class, "getBoolean", MethodType.methodType(boolean.class, byte[].class, int.class))
short.class, lkp.findStatic(ByteArray.class, "getShort", MethodType.methodType(short.class, byte[].class, int.class)),
int.class, lkp.findStatic(ByteArray.class, "getInt", MethodType.methodType(int.class, byte[].class, int.class)),
long.class, lkp.findStatic(ByteArray.class, "getLong", MethodType.methodType(long.class, byte[].class, int.class)),
float.class, lkp.findStatic(ByteArray.class, "getFloat", MethodType.methodType(float.class, byte[].class, int.class)),
double.class, lkp.findStatic(ByteArray.class, "getDouble", MethodType.methodType(double.class, byte[].class, int.class)),
char.class, lkp.findStatic(ByteArray.class, "getChar", MethodType.methodType(char.class, byte[].class, int.class)),
boolean.class, lkp.findStatic(ByteArray.class, "getBoolean", MethodType.methodType(boolean.class, byte[].class, int.class))
);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new InternalError("Can't lookup Bits.getXXX", e);
throw new InternalError("Can't lookup " + ByteArray.class.getName() + ".getXXX", e);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 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
@ -30,6 +30,7 @@ import java.nio.channels.FileChannel;
import jdk.internal.access.JavaIORandomAccessFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Blocker;
import jdk.internal.util.ByteArray;
import sun.nio.ch.FileChannelImpl;
@ -884,7 +885,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
*/
public final int readInt() throws IOException {
readFully(buffer, 0, Integer.BYTES);
return Bits.getInt(buffer, 0);
return ByteArray.getInt(buffer, 0);
}
/**
@ -917,7 +918,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
*/
public final long readLong() throws IOException {
readFully(buffer, 0, Long.BYTES);
return Bits.getLong(buffer, 0);
return ByteArray.getLong(buffer, 0);
}
/**
@ -941,7 +942,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
*/
public final float readFloat() throws IOException {
readFully(buffer, 0, Float.BYTES);
return Bits.getFloat(buffer, 0);
return ByteArray.getFloat(buffer, 0);
}
/**
@ -965,7 +966,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
*/
public final double readDouble() throws IOException {
readFully(buffer, 0, Double.BYTES);
return Bits.getDouble(buffer, 0);
return ByteArray.getDouble(buffer, 0);
}
/**
@ -1104,7 +1105,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* @throws IOException if an I/O error occurs.
*/
public final void writeInt(int v) throws IOException {
Bits.putInt(buffer, 0, v);
ByteArray.setInt(buffer, 0, v);
write(buffer, 0, Integer.BYTES);
//written += 4;
}
@ -1117,7 +1118,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* @throws IOException if an I/O error occurs.
*/
public final void writeLong(long v) throws IOException {
Bits.putLong(buffer, 0, v);
ByteArray.setLong(buffer, 0, v);
write(buffer, 0, Long.BYTES);
}
@ -1133,7 +1134,8 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* @see java.lang.Float#floatToIntBits(float)
*/
public final void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
ByteArray.setFloat(buffer, 0, v);
write(buffer, 0, Float.BYTES);
}
/**
@ -1148,7 +1150,8 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* @see java.lang.Double#doubleToLongBits(double)
*/
public final void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
ByteArray.setDouble(buffer, 0, v);
write(buffer, 0, Double.BYTES);
}
/**

View File

@ -0,0 +1,424 @@
/*
* 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#BIG_ENDIAN big endian order} (aka. "network 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 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);
/*
* 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 big 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 create(Class<?> viewArrayClass) {
return MethodHandles.byteArrayViewVarHandle(viewArrayClass, ByteOrder.BIG_ENDIAN);
}
}

View File

@ -1,88 +0,0 @@
/*
* 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.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.io;
/**
* Class to allow public access to package-private methods.
*/
public final class BitsProxy {
public static boolean getBoolean(byte[] b, int off) {
return Bits.getBoolean(b, off);
}
public static char getChar(byte[] b, int off) {
return Bits.getChar(b, off);
}
public static short getShort(byte[] b, int off) {
return Bits.getShort(b, off);
}
public static int getInt(byte[] b, int off) {
return Bits.getInt(b, off);
}
public static float getFloat(byte[] b, int off) {
return Bits.getFloat(b, off);
}
public static long getLong(byte[] b, int off) {
return Bits.getLong(b, off);
}
public static double getDouble(byte[] b, int off) {
return Bits.getDouble(b, off);
}
public static void putBoolean(byte[] b, int off, boolean val) {
Bits.putBoolean(b, off, val);
}
public static void putChar(byte[] b, int off, char val) {
Bits.putChar(b, off, val);
}
public static void putShort(byte[] b, int off, short val) {
Bits.putShort(b, off, val);
}
public static void putInt(byte[] b, int off, int val) {
Bits.putInt(b, off, val);
}
public static void putFloat(byte[] b, int off, float val) {
Bits.putFloat(b, off, val);
}
public static void putLong(byte[] b, int off, long val) {
Bits.putLong(b, off, val);
}
public static void putDouble(byte[] b, int off, double val) {
Bits.putDouble(b, off, val);
}
}

View File

@ -24,19 +24,19 @@
/*
* @test
* @bug 8299576
* @modules java.base/jdk.internal.util
* @summary Verify that reads and writes of primitives are correct
* @compile/module=java.base java/io/BitsProxy.java
* @run junit ReadWriteValues
*/
import java.io.BitsProxy;
import jdk.internal.util.ByteArray;
import org.junit.jupiter.api.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.DoubleStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
final class ReadWriteValues {
@ -53,7 +53,7 @@ final class ReadWriteValues {
longs().forEach(l -> {
short expected = (short) l;
RefImpl.putShort(BUFF, OFFSET, expected);
short actual = BitsProxy.getShort(BUFF, OFFSET);
short actual = ByteArray.getShort(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -62,7 +62,7 @@ final class ReadWriteValues {
void testPutShort() {
longs().forEach(l -> {
short expected = (short) l;
BitsProxy.putShort(BUFF, OFFSET, expected);
ByteArray.setShort(BUFF, OFFSET, expected);
short actual = RefImpl.getShort(BUFF, OFFSET);
assertEquals(expected, actual);
});
@ -73,7 +73,7 @@ final class ReadWriteValues {
longs().forEach(l -> {
char expected = (char) l;
RefImpl.putChar(BUFF, OFFSET, expected);
char actual = BitsProxy.getChar(BUFF, OFFSET);
char actual = ByteArray.getChar(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -82,7 +82,7 @@ final class ReadWriteValues {
void testPutChar() {
longs().forEach(l -> {
char expected = (char) l;
BitsProxy.putChar(BUFF, OFFSET, expected);
ByteArray.setChar(BUFF, OFFSET, expected);
char actual = RefImpl.getChar(BUFF, OFFSET);
assertEquals(expected, actual);
});
@ -93,7 +93,7 @@ final class ReadWriteValues {
longs().forEach(l -> {
int expected = (int) l;
RefImpl.putInt(BUFF, OFFSET, expected);
int actual = BitsProxy.getInt(BUFF, OFFSET);
int actual = ByteArray.getInt(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -102,7 +102,7 @@ final class ReadWriteValues {
void testPutInt() {
longs().forEach(l -> {
int expected = (int) l;
BitsProxy.putInt(BUFF, OFFSET, expected);
ByteArray.setInt(BUFF, OFFSET, expected);
int actual = RefImpl.getInt(BUFF, OFFSET);
assertEquals(expected, actual);
});
@ -112,7 +112,7 @@ final class ReadWriteValues {
void testGetLong() {
longs().forEach(expected -> {
RefImpl.putLong(BUFF, OFFSET, expected);
long actual = BitsProxy.getLong(BUFF, OFFSET);
long actual = ByteArray.getLong(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -120,7 +120,7 @@ final class ReadWriteValues {
@Test
void testPutLong() {
longs().forEach(expected -> {
BitsProxy.putLong(BUFF, OFFSET, expected);
ByteArray.setLong(BUFF, OFFSET, expected);
long actual = RefImpl.getLong(BUFF, OFFSET);
assertEquals(expected, actual);
});
@ -130,7 +130,7 @@ final class ReadWriteValues {
void testGetFloat() {
floats().forEach(expected -> {
RefImpl.putFloat(BUFF, OFFSET, expected);
float actual = BitsProxy.getFloat(BUFF, OFFSET);
float actual = ByteArray.getFloat(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -138,7 +138,7 @@ final class ReadWriteValues {
@Test
void testPutFloat() {
floats().forEach(expected -> {
BitsProxy.putFloat(BUFF, OFFSET, expected);
ByteArray.setFloat(BUFF, OFFSET, expected);
float actual = RefImpl.getFloat(BUFF, OFFSET);
assertEquals(expected, actual);
});
@ -148,7 +148,7 @@ final class ReadWriteValues {
void testGetDouble() {
doubles().forEach(expected -> {
RefImpl.putDouble(BUFF, OFFSET, expected);
double actual = BitsProxy.getDouble(BUFF, OFFSET);
double actual = ByteArray.getDouble(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@ -156,31 +156,183 @@ final class ReadWriteValues {
@Test
void testPutDouble() {
doubles().forEach(expected -> {
BitsProxy.putDouble(BUFF, OFFSET, expected);
ByteArray.setDouble(BUFF, OFFSET, expected);
double actual = RefImpl.getDouble(BUFF, OFFSET);
assertEquals(expected, actual);
});
}
@Test
void testPutUnsignedShort() {
longs().forEach(l -> {
int expected = Short.toUnsignedInt((short) l);
ByteArray.setUnsignedShort(BUFF, OFFSET, expected);
int actual = Short.toUnsignedInt(RefImpl.getShort(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));
}
@Test
void testNegArg() {
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.getInt(BUFF, -1));
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.setInt(BUFF, -1, 1));
}
@Test
void testOutOfBounds() {
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.getInt(BUFF, BUFF.length));
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.setInt(BUFF, BUFF.length, 1));
}
// At-zero methods
@Test
void testGetShortAtZero() {
longs().forEach(l -> {
short expected = (short) l;
RefImpl.putShort(BUFF, 0, expected);
short actual = ByteArray.getShort(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutShortAtZero() {
longs().forEach(l -> {
short expected = (short) l;
ByteArray.setShort(BUFF, expected);
short actual = RefImpl.getShort(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testGetCharAtZero() {
longs().forEach(l -> {
char expected = (char) l;
RefImpl.putChar(BUFF, 0, expected);
char actual = ByteArray.getChar(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutCharAtZero() {
longs().forEach(l -> {
char expected = (char) l;
ByteArray.setChar(BUFF, expected);
char actual = RefImpl.getChar(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testGetIntAtZero() {
longs().forEach(l -> {
int expected = (int) l;
RefImpl.putInt(BUFF, 0, expected);
int actual = ByteArray.getInt(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutIntAtZero() {
longs().forEach(l -> {
int expected = (int) l;
ByteArray.setInt(BUFF, expected);
int actual = RefImpl.getInt(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testGetLongAtZero() {
longs().forEach(expected -> {
RefImpl.putLong(BUFF, 0, expected);
long actual = ByteArray.getLong(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutLongAtZero() {
longs().forEach(expected -> {
ByteArray.setLong(BUFF, expected);
long actual = RefImpl.getLong(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testGetFloatAtZero() {
floats().forEach(expected -> {
RefImpl.putFloat(BUFF, 0, expected);
float actual = ByteArray.getFloat(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutFloatAtZero() {
floats().forEach(expected -> {
ByteArray.setFloat(BUFF, expected);
float actual = RefImpl.getFloat(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testGetDoubleAtZero() {
doubles().forEach(expected -> {
RefImpl.putDouble(BUFF, 0, expected);
double actual = ByteArray.getDouble(BUFF);
assertEquals(expected, actual);
});
}
@Test
void testPutDoubleAtZero() {
doubles().forEach(expected -> {
ByteArray.setDouble(BUFF, expected);
double actual = RefImpl.getDouble(BUFF, 0);
assertEquals(expected, actual);
});
}
@Test
void testPutUnsignedShortAtZero() {
longs().forEach(l -> {
int expected = Short.toUnsignedInt((short) l);
ByteArray.setUnsignedShort(BUFF, expected);
int actual = Short.toUnsignedInt(RefImpl.getShort(BUFF, 0));
assertEquals(expected, actual);
});
}
// Unusual cases
@Test
void testNullArray() {
assertThrowsOriginal(NullPointerException.class, () -> BitsProxy.getInt(null, OFFSET));
assertThrowsOriginal(NullPointerException.class, () -> BitsProxy.putInt(null, OFFSET, 1));
void testNullArrayAtZero() {
assertThrowsOriginal(NullPointerException.class, () -> ByteArray.getInt(null));
assertThrowsOriginal(NullPointerException.class, () -> ByteArray.setInt(null, 1));
}
@Test
void testNegArg() {
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> BitsProxy.getInt(BUFF, -1));
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> BitsProxy.putInt(BUFF, -1, 1));
void testOutOfBoundsAtZero() {
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.getInt(new byte[1]));
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> ByteArray.setInt(new byte[1],1));
}
@Test
void testOutOfBounds() {
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> BitsProxy.getInt(BUFF, BUFF.length));
assertThrowsOriginal(IndexOutOfBoundsException.class, () -> BitsProxy.putInt(BUFF, BUFF.length, 1));
}
static LongStream longs() {
return ThreadLocalRandom.current().longs(ITERATIONS);
@ -315,5 +467,4 @@ final class ReadWriteValues {
putLong(b, off, Double.doubleToLongBits(val));
}
}
}

View File

@ -0,0 +1,150 @@
/*
* 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.
*
* 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 org.openjdk.bench.java.io;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(2)
@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class PrimitiveFieldSerializationBenchmark {
public static void main(String[] args) throws Exception {
Options options = new OptionsBuilder()
.include(PrimitiveFieldSerializationBenchmark.class.getSimpleName())
.build();
new Runner(options).run();
}
@State(Scope.Benchmark)
public static class Log {
MyData myData = new MyData((byte) 1, 'a', (short) 47, 1234, 0.01f, 1234L, 0.01d);
MyRecord myRecord = new MyRecord((byte) 1, 'a', (short) 47, 1234, 0.01f, 1234L, 0.01d);
}
private OutputStream bos;
private ObjectOutputStream os;
@Setup
public void setupStreams(Blackhole bh) throws IOException {
bos = new BlackholeOutputStream(bh);
os = new ObjectOutputStream(bos);
}
@TearDown
public void tearDownStreams() throws IOException {
os.close();
bos.close();
}
private static final class MyData implements Serializable {
byte b;
char c;
short s;
int i;
float f;
long l;
double d;
public MyData(byte b, char c, short s, int i, float f, long l, double d) {
this.b = b;
this.c = c;
this.s = s;
this.i = i;
this.f = f;
this.l = l;
this.d = d;
}
}
private record MyRecord(byte b,
char c,
short s,
int i,
float f,
long l,
double d) implements Serializable {
}
@Benchmark
public void serializeData(Log input) throws IOException {
os.writeObject(input.myData);
}
@Benchmark
public void serializeRecord(Log input) throws IOException {
os.writeObject(input.myRecord);
}
public static final class BlackholeOutputStream extends OutputStream {
private final Blackhole bh;
public BlackholeOutputStream(Blackhole bh) {
this.bh = bh;
}
@Override
public void write(int b) {
bh.consume(b);
}
@Override
public void write(byte[] b) {
bh.consume(b);
}
@Override
public void write(byte[] b, int off, int len) {
bh.consume(b);
}
}
}