diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java index e20a8858c22aa..489d923b350e6 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java @@ -1504,7 +1504,10 @@ public String readString() throws IOException { // TODO(anuraaga): It might be possible to share the optimized loop with // readStringRequireUtf8 by implementing Java replacement logic there. // The same as readBytes' logic - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); String result = new String(bytes, UTF_8); pos += size; @@ -1621,7 +1624,10 @@ public ByteString readBytes() throws IOException { return ByteString.wrap(result); } else { // Use UnsafeUtil to copy the memory to bytes instead of using ByteBuffer ways. - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); pos += size; return ByteString.wrap(bytes); @@ -1655,7 +1661,10 @@ public ByteBuffer readByteBuffer() throws IOException { return result; } else { // The same as readBytes' logic - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); pos += size; return ByteBuffer.wrap(bytes); @@ -1954,7 +1963,10 @@ public byte readRawByte() throws IOException { @Override public byte[] readRawBytes(final int length) throws IOException { if (length >= 0 && length <= remaining()) { - byte[] bytes = new byte[length]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(length) + : new byte[length]; slice(pos, pos + length).get(bytes); pos += length; return bytes; @@ -3348,14 +3360,20 @@ public boolean readBool() throws IOException { public String readString() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) { - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); String result = new String(bytes, UTF_8); currentByteBufferPos += size; return result; } else if (size > 0 && size <= remaining()) { // TODO(yilunchong): To use an underlying bytes[] instead of allocating a new bytes[] - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; readRawBytesTo(bytes, 0, size); String result = new String(bytes, UTF_8); return result; @@ -3476,14 +3494,19 @@ public ByteString readBytes() throws IOException { currentByteBufferPos += size; return result; } else { - byte[] bytes; - bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); currentByteBufferPos += size; return ByteString.wrap(bytes); } } else if (size > 0 && size <= remaining()) { - byte[] temp = new byte[size]; + byte[] temp = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; readRawBytesTo(temp, 0, size); return ByteString.wrap(temp); } @@ -3512,13 +3535,19 @@ public ByteBuffer readByteBuffer() throws IOException { (int) (currentByteBufferPos - currentAddress - size), (int) (currentByteBufferPos - currentAddress)); } else { - byte[] bytes = new byte[size]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); currentByteBufferPos += size; return ByteBuffer.wrap(bytes); } } else if (size > 0 && size <= remaining()) { - byte[] temp = new byte[size]; + byte[] temp = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(size) + : new byte[size]; readRawBytesTo(temp, 0, size); return ByteBuffer.wrap(temp); } @@ -3793,13 +3822,19 @@ public byte readRawByte() throws IOException { @Override public byte[] readRawBytes(final int length) throws IOException { if (length >= 0 && length <= currentRemaining()) { - byte[] bytes = new byte[length]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(length) + : new byte[length]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, length); currentByteBufferPos += length; return bytes; } if (length >= 0 && length <= remaining()) { - byte[] bytes = new byte[length]; + byte[] bytes = + UnsafeUtil.hasUnsafeAllocateArrayOperation() + ? UnsafeUtil.allocateUninitializedArray(length) + : new byte[length]; readRawBytesTo(bytes, 0, length); return bytes; } diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java index b435327ab8b85..6b16a44c0173d 100644 --- a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java +++ b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java @@ -31,10 +31,13 @@ package com.google.protobuf; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.util.logging.Level; import java.util.logging.Logger; @@ -43,6 +46,8 @@ final class UnsafeUtil { private static final Logger logger = Logger.getLogger(UnsafeUtil.class.getName()); private static final sun.misc.Unsafe UNSAFE = getUnsafe(); + private static final Object INTERNAL_UNSAFE = getInternalUnsafe(); + private static final Method ALLOCATE_ARRAY_METHOD = getAllocateArrayMethod(); private static final Class MEMORY_CLASS = Android.getMemoryClass(); private static final boolean IS_ANDROID_64 = determineAndroidSupportByAddressSize(long.class); private static final boolean IS_ANDROID_32 = determineAndroidSupportByAddressSize(int.class); @@ -92,6 +97,10 @@ static boolean hasUnsafeByteBufferOperations() { return HAS_UNSAFE_BYTEBUFFER_OPERATIONS; } + static boolean hasUnsafeAllocateArrayOperation() { + return ALLOCATE_ARRAY_METHOD != null; + } + static boolean isAndroid64() { return IS_ANDROID_64; } @@ -251,6 +260,10 @@ static void copyMemory(byte[] src, long srcIndex, byte[] target, long targetInde System.arraycopy(src, (int) srcIndex, target, (int) targetIndex, (int) length); } + static byte[] allocateUninitializedArray(int size) { + return MEMORY_ACCESSOR.allocateUninitializedArray(size); + } + static byte getByte(long address) { return MEMORY_ACCESSOR.getByte(address); } @@ -315,6 +328,60 @@ public sun.misc.Unsafe run() throws Exception { return unsafe; } + static Object getInternalUnsafe() { + Object internalUnsafe = null; + try { + internalUnsafe = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + Class k = + getClassLoader(UnsafeUtil.class).loadClass("jdk.internal.misc.Unsafe"); + Method m = k.getDeclaredMethod("getUnsafe"); + return m.invoke(null); + } + }); + } catch (Throwable e) { + // Catching Throwable for Java 8. + } + return internalUnsafe; + } + + static Method getAllocateArrayMethod() { + if (INTERNAL_UNSAFE == null) { + return null; + } + Method allocateArrayMethod = null; + try { + allocateArrayMethod = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Method run() throws Exception { + return INTERNAL_UNSAFE.getClass().getDeclaredMethod( + "allocateUninitializedArray", Class.class, int.class); + } + }); + } catch (Throwable e) { + // Catching Throwable for Java 8. + } + return allocateArrayMethod; + } + + private static ClassLoader getClassLoader(final Class clazz) { + if (System.getSecurityManager() == null) { + return clazz.getClassLoader(); + } else { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + return clazz.getClassLoader(); + } + }); + } + } + /** Get a {@link MemoryAccessor} appropriate for the platform, or null if not supported. */ private static MemoryAccessor getMemoryAccessor() { if (UNSAFE == null) { @@ -330,7 +397,7 @@ private static MemoryAccessor getMemoryAccessor() { } } - return new JvmMemoryAccessor(UNSAFE); + return new JvmMemoryAccessor(UNSAFE, INTERNAL_UNSAFE, ALLOCATE_ARRAY_METHOD); } /** Indicates whether or not unsafe array operations are supported on this platform. */ @@ -611,12 +678,19 @@ public final int arrayIndexScale(Class clazz) { public abstract void copyMemory(long srcOffset, byte[] target, long targetIndex, long length); public abstract void copyMemory(byte[] src, long srcIndex, long targetOffset, long length); + + public abstract byte[] allocateUninitializedArray(int size); } private static final class JvmMemoryAccessor extends MemoryAccessor { - JvmMemoryAccessor(sun.misc.Unsafe unsafe) { + Object internalUnsafe; + Method allocateArrayMethod; + + JvmMemoryAccessor(sun.misc.Unsafe unsafe, Object internalUnsafe, Method allocateArrayMethod) { super(unsafe); + this.internalUnsafe = internalUnsafe; + this.allocateArrayMethod = allocateArrayMethod; } @Override @@ -699,6 +773,17 @@ public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length unsafe.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, targetOffset, length); } + @Override + public byte[] allocateUninitializedArray(int size) { + try { + return (byte[]) allocateArrayMethod.invoke(internalUnsafe, byte.class, size); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + @Override public Object getStaticObject(Field field) { return getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); @@ -807,6 +892,11 @@ public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length throw new UnsupportedOperationException(); } + @Override + public byte[] allocateUninitializedArray(int size) { + throw new UnsupportedOperationException(); + } + @Override public Object getStaticObject(Field field) { try { @@ -927,6 +1017,11 @@ public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length throw new UnsupportedOperationException(); } + @Override + public byte[] allocateUninitializedArray(int size) { + throw new UnsupportedOperationException(); + } + @Override public Object getStaticObject(Field field) { try {