| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package com.google.protobuf; |
| |
| import java.lang.reflect.Field; |
| import java.nio.Buffer; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.security.AccessController; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** Utility class for working with unsafe operations. */ |
| final class UnsafeUtil { |
| private static final Logger logger = Logger.getLogger(UnsafeUtil.class.getName()); |
| private static final sun.misc.Unsafe UNSAFE = getUnsafe(); |
| private static final MemoryAccessor MEMORY_ACCESSOR = getMemoryAccessor(); |
| private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS = |
| supportsUnsafeByteBufferOperations(); |
| private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); |
| |
| private static final long BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset(byte[].class); |
| // Micro-optimization: we can assume a scale of 1 and skip the multiply |
| // private static final long BYTE_ARRAY_INDEX_SCALE = 1; |
| |
| private static final long BOOLEAN_ARRAY_BASE_OFFSET = arrayBaseOffset(boolean[].class); |
| private static final long BOOLEAN_ARRAY_INDEX_SCALE = arrayIndexScale(boolean[].class); |
| |
| private static final long INT_ARRAY_BASE_OFFSET = arrayBaseOffset(int[].class); |
| private static final long INT_ARRAY_INDEX_SCALE = arrayIndexScale(int[].class); |
| |
| private static final long LONG_ARRAY_BASE_OFFSET = arrayBaseOffset(long[].class); |
| private static final long LONG_ARRAY_INDEX_SCALE = arrayIndexScale(long[].class); |
| |
| private static final long FLOAT_ARRAY_BASE_OFFSET = arrayBaseOffset(float[].class); |
| private static final long FLOAT_ARRAY_INDEX_SCALE = arrayIndexScale(float[].class); |
| |
| private static final long DOUBLE_ARRAY_BASE_OFFSET = arrayBaseOffset(double[].class); |
| private static final long DOUBLE_ARRAY_INDEX_SCALE = arrayIndexScale(double[].class); |
| |
| private static final long OBJECT_ARRAY_BASE_OFFSET = arrayBaseOffset(Object[].class); |
| private static final long OBJECT_ARRAY_INDEX_SCALE = arrayIndexScale(Object[].class); |
| |
| private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(bufferAddressField()); |
| |
| private static final long STRING_VALUE_OFFSET = fieldOffset(stringValueField()); |
| |
| private UnsafeUtil() {} |
| |
| static boolean hasUnsafeArrayOperations() { |
| return HAS_UNSAFE_ARRAY_OPERATIONS; |
| } |
| |
| static boolean hasUnsafeByteBufferOperations() { |
| return HAS_UNSAFE_BYTEBUFFER_OPERATIONS; |
| } |
| |
| |
| static long objectFieldOffset(Field field) { |
| return MEMORY_ACCESSOR.objectFieldOffset(field); |
| } |
| |
| private static int arrayBaseOffset(Class<?> clazz) { |
| return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayBaseOffset(clazz) : -1; |
| } |
| |
| private static int arrayIndexScale(Class<?> clazz) { |
| return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayIndexScale(clazz) : -1; |
| } |
| |
| static byte getByte(Object target, long offset) { |
| return MEMORY_ACCESSOR.getByte(target, offset); |
| } |
| |
| static void putByte(Object target, long offset, byte value) { |
| MEMORY_ACCESSOR.putByte(target, offset, value); |
| } |
| |
| static int getInt(Object target, long offset) { |
| return MEMORY_ACCESSOR.getInt(target, offset); |
| } |
| |
| static void putInt(Object target, long offset, int value) { |
| MEMORY_ACCESSOR.putInt(target, offset, value); |
| } |
| |
| static long getLong(Object target, long offset) { |
| return MEMORY_ACCESSOR.getLong(target, offset); |
| } |
| |
| static void putLong(Object target, long offset, long value) { |
| MEMORY_ACCESSOR.putLong(target, offset, value); |
| } |
| |
| static boolean getBoolean(Object target, long offset) { |
| return MEMORY_ACCESSOR.getBoolean(target, offset); |
| } |
| |
| static void putBoolean(Object target, long offset, boolean value) { |
| MEMORY_ACCESSOR.putBoolean(target, offset, value); |
| } |
| |
| static float getFloat(Object target, long offset) { |
| return MEMORY_ACCESSOR.getFloat(target, offset); |
| } |
| |
| static void putFloat(Object target, long offset, float value) { |
| MEMORY_ACCESSOR.putFloat(target, offset, value); |
| } |
| |
| static double getDouble(Object target, long offset) { |
| return MEMORY_ACCESSOR.getDouble(target, offset); |
| } |
| |
| static void putDouble(Object target, long offset, double value) { |
| MEMORY_ACCESSOR.putDouble(target, offset, value); |
| } |
| |
| static Object getObject(Object target, long offset) { |
| return MEMORY_ACCESSOR.getObject(target, offset); |
| } |
| |
| static void putObject(Object target, long offset, Object value) { |
| MEMORY_ACCESSOR.putObject(target, offset, value); |
| } |
| |
| static byte getByte(byte[] target, long index) { |
| return MEMORY_ACCESSOR.getByte(target, BYTE_ARRAY_BASE_OFFSET + index); |
| } |
| |
| static void putByte(byte[] target, long index, byte value) { |
| MEMORY_ACCESSOR.putByte(target, BYTE_ARRAY_BASE_OFFSET + index, value); |
| } |
| |
| static int getInt(int[] target, long index) { |
| return MEMORY_ACCESSOR.getInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putInt(int[] target, long index, int value) { |
| MEMORY_ACCESSOR.putInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static long getLong(long[] target, long index) { |
| return MEMORY_ACCESSOR.getLong( |
| target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putLong(long[] target, long index, long value) { |
| MEMORY_ACCESSOR.putLong( |
| target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static boolean getBoolean(boolean[] target, long index) { |
| return MEMORY_ACCESSOR.getBoolean( |
| target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putBoolean(boolean[] target, long index, boolean value) { |
| MEMORY_ACCESSOR.putBoolean( |
| target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static float getFloat(float[] target, long index) { |
| return MEMORY_ACCESSOR.getFloat( |
| target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putFloat(float[] target, long index, float value) { |
| MEMORY_ACCESSOR.putFloat( |
| target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static double getDouble(double[] target, long index) { |
| return MEMORY_ACCESSOR.getDouble( |
| target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putDouble(double[] target, long index, double value) { |
| MEMORY_ACCESSOR.putDouble( |
| target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static Object getObject(Object[] target, long index) { |
| return MEMORY_ACCESSOR.getObject( |
| target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE)); |
| } |
| |
| static void putObject(Object[] target, long index, Object value) { |
| MEMORY_ACCESSOR.putObject( |
| target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE), value); |
| } |
| |
| static void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) { |
| MEMORY_ACCESSOR.copyMemory(src, srcIndex, targetOffset, length); |
| } |
| |
| static void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) { |
| MEMORY_ACCESSOR.copyMemory(srcOffset, target, targetIndex, length); |
| } |
| |
| static void copyMemory(byte[] src, long srcIndex, byte[] target, long targetIndex, long length) { |
| System.arraycopy(src, (int) srcIndex, target, (int) targetIndex, (int) length); |
| } |
| |
| static byte getByte(long address) { |
| return MEMORY_ACCESSOR.getByte(address); |
| } |
| |
| static void putByte(long address, byte value) { |
| MEMORY_ACCESSOR.putByte(address, value); |
| } |
| |
| static int getInt(long address) { |
| return MEMORY_ACCESSOR.getInt(address); |
| } |
| |
| static void putInt(long address, int value) { |
| MEMORY_ACCESSOR.putInt(address, value); |
| } |
| |
| static long getLong(long address) { |
| return MEMORY_ACCESSOR.getLong(address); |
| } |
| |
| static void putLong(long address, long value) { |
| MEMORY_ACCESSOR.putLong(address, value); |
| } |
| |
| /** |
| * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}. |
| */ |
| static long addressOffset(ByteBuffer buffer) { |
| return MEMORY_ACCESSOR.getLong(buffer, BUFFER_ADDRESS_OFFSET); |
| } |
| |
| /** |
| * Returns a new {@link String} backed by the given {@code chars}. The char array should not |
| * be mutated any more after calling this function. |
| */ |
| static String moveToString(char[] chars) { |
| if (STRING_VALUE_OFFSET == -1) { |
| // In the off-chance that this JDK does not implement String as we'd expect, just do a copy. |
| return new String(chars); |
| } |
| final String str; |
| try { |
| str = (String) UNSAFE.allocateInstance(String.class); |
| } catch (InstantiationException e) { |
| // This should never happen, but return a copy as a fallback just in case. |
| return new String(chars); |
| } |
| putObject(str, STRING_VALUE_OFFSET, chars); |
| return str; |
| } |
| |
| static Object getStaticObject(Field field) { |
| return MEMORY_ACCESSOR.getStaticObject(field); |
| } |
| |
| /** |
| * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform. |
| */ |
| static sun.misc.Unsafe getUnsafe() { |
| sun.misc.Unsafe unsafe = null; |
| try { |
| unsafe = |
| AccessController.doPrivileged( |
| new PrivilegedExceptionAction<sun.misc.Unsafe>() { |
| @Override |
| public sun.misc.Unsafe run() throws Exception { |
| Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; |
| |
| for (Field f : k.getDeclaredFields()) { |
| f.setAccessible(true); |
| Object x = f.get(null); |
| if (k.isInstance(x)) { |
| return k.cast(x); |
| } |
| } |
| // The sun.misc.Unsafe field does not exist. |
| return null; |
| } |
| }); |
| } catch (Throwable e) { |
| // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError |
| // for Unsafe. |
| } |
| return unsafe; |
| } |
| |
| /** Get a {@link MemoryAccessor} appropriate for the platform, or null if not supported. */ |
| private static MemoryAccessor getMemoryAccessor() { |
| if (UNSAFE == null) { |
| return null; |
| } |
| return new JvmMemoryAccessor(UNSAFE); |
| } |
| |
| /** Indicates whether or not unsafe array operations are supported on this platform. */ |
| private static boolean supportsUnsafeArrayOperations() { |
| if (UNSAFE == null) { |
| return false; |
| } |
| try { |
| Class<?> clazz = UNSAFE.getClass(); |
| clazz.getMethod("objectFieldOffset", Field.class); |
| clazz.getMethod("arrayBaseOffset", Class.class); |
| clazz.getMethod("arrayIndexScale", Class.class); |
| clazz.getMethod("getInt", Object.class, long.class); |
| clazz.getMethod("putInt", Object.class, long.class, int.class); |
| clazz.getMethod("getLong", Object.class, long.class); |
| clazz.getMethod("putLong", Object.class, long.class, long.class); |
| clazz.getMethod("getObject", Object.class, long.class); |
| clazz.getMethod("putObject", Object.class, long.class, Object.class); |
| clazz.getMethod("getByte", Object.class, long.class); |
| clazz.getMethod("putByte", Object.class, long.class, byte.class); |
| clazz.getMethod("getBoolean", Object.class, long.class); |
| clazz.getMethod("putBoolean", Object.class, long.class, boolean.class); |
| clazz.getMethod("getFloat", Object.class, long.class); |
| clazz.getMethod("putFloat", Object.class, long.class, float.class); |
| clazz.getMethod("getDouble", Object.class, long.class); |
| clazz.getMethod("putDouble", Object.class, long.class, double.class); |
| |
| return true; |
| } catch (Throwable e) { |
| logger.log( |
| Level.WARNING, |
| "platform method missing - proto runtime falling back to safer methods: " + e); |
| } |
| return false; |
| } |
| |
| private static boolean supportsUnsafeByteBufferOperations() { |
| if (UNSAFE == null) { |
| return false; |
| } |
| try { |
| Class<?> clazz = UNSAFE.getClass(); |
| // Methods for getting direct buffer address. |
| clazz.getMethod("objectFieldOffset", Field.class); |
| clazz.getMethod("getLong", Object.class, long.class); |
| |
| if (bufferAddressField() == null) { |
| return false; |
| } |
| |
| clazz.getMethod("getByte", long.class); |
| clazz.getMethod("putByte", long.class, byte.class); |
| clazz.getMethod("getInt", long.class); |
| clazz.getMethod("putInt", long.class, int.class); |
| clazz.getMethod("getLong", long.class); |
| clazz.getMethod("putLong", long.class, long.class); |
| clazz.getMethod("copyMemory", long.class, long.class, long.class); |
| clazz.getMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class); |
| return true; |
| } catch (Throwable e) { |
| logger.log( |
| Level.WARNING, |
| "platform method missing - proto runtime falling back to safer methods: " + e); |
| } |
| return false; |
| } |
| |
| |
| /** Finds the address field within a direct {@link Buffer}. */ |
| private static Field bufferAddressField() { |
| Field field = field(Buffer.class, "address"); |
| return field != null && field.getType() == long.class ? field : null; |
| } |
| |
| /** Finds the value field within a {@link String}. */ |
| private static Field stringValueField() { |
| Field field = field(String.class, "value"); |
| return field != null && field.getType() == char[].class ? field : null; |
| } |
| |
| /** |
| * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not |
| * available. |
| */ |
| private static long fieldOffset(Field field) { |
| return field == null || MEMORY_ACCESSOR == null ? -1 : MEMORY_ACCESSOR.objectFieldOffset(field); |
| } |
| |
| /** |
| * Gets the field with the given name within the class, or {@code null} if not found. If found, |
| * the field is made accessible. |
| */ |
| private static Field field(Class<?> clazz, String fieldName) { |
| Field field; |
| try { |
| field = clazz.getDeclaredField(fieldName); |
| field.setAccessible(true); |
| } catch (Throwable t) { |
| // Failed to access the fields. |
| field = null; |
| } |
| return field; |
| } |
| |
| private abstract static class MemoryAccessor { |
| |
| sun.misc.Unsafe unsafe; |
| |
| MemoryAccessor(sun.misc.Unsafe unsafe) { |
| this.unsafe = unsafe; |
| } |
| |
| public final long objectFieldOffset(Field field) { |
| return unsafe.objectFieldOffset(field); |
| } |
| |
| public abstract byte getByte(Object target, long offset); |
| |
| public abstract void putByte(Object target, long offset, byte value); |
| |
| public final int getInt(Object target, long offset) { |
| return unsafe.getInt(target, offset); |
| } |
| |
| public final void putInt(Object target, long offset, int value) { |
| unsafe.putInt(target, offset, value); |
| } |
| |
| public final long getLong(Object target, long offset) { |
| return unsafe.getLong(target, offset); |
| } |
| |
| public final void putLong(Object target, long offset, long value) { |
| unsafe.putLong(target, offset, value); |
| } |
| |
| public abstract boolean getBoolean(Object target, long offset); |
| |
| public abstract void putBoolean(Object target, long offset, boolean value); |
| |
| public abstract float getFloat(Object target, long offset); |
| |
| public abstract void putFloat(Object target, long offset, float value); |
| |
| public abstract double getDouble(Object target, long offset); |
| |
| public abstract void putDouble(Object target, long offset, double value); |
| |
| public final Object getObject(Object target, long offset) { |
| return unsafe.getObject(target, offset); |
| } |
| |
| public final void putObject(Object target, long offset, Object value) { |
| unsafe.putObject(target, offset, value); |
| } |
| |
| public final int arrayBaseOffset(Class<?> clazz) { |
| return unsafe.arrayBaseOffset(clazz); |
| } |
| |
| public final int arrayIndexScale(Class<?> clazz) { |
| return unsafe.arrayIndexScale(clazz); |
| } |
| |
| public abstract byte getByte(long address); |
| |
| public abstract void putByte(long address, byte value); |
| |
| public abstract int getInt(long address); |
| |
| public abstract void putInt(long address, int value); |
| |
| public abstract long getLong(long address); |
| |
| public abstract void putLong(long address, long value); |
| |
| public abstract Object getStaticObject(Field field); |
| |
| public abstract void copyMemory(long srcOffset, byte[] target, long targetIndex, long length); |
| |
| public abstract void copyMemory(byte[] src, long srcIndex, long targetOffset, long length); |
| } |
| |
| private static final class JvmMemoryAccessor extends MemoryAccessor { |
| |
| JvmMemoryAccessor(sun.misc.Unsafe unsafe) { |
| super(unsafe); |
| } |
| |
| @Override |
| public byte getByte(long address) { |
| return unsafe.getByte(address); |
| } |
| |
| @Override |
| public void putByte(long address, byte value) { |
| unsafe.putByte(address, value); |
| } |
| |
| @Override |
| public int getInt(long address) { |
| return unsafe.getInt(address); |
| } |
| |
| @Override |
| public void putInt(long address, int value) { |
| unsafe.putInt(address, value); |
| } |
| |
| @Override |
| public long getLong(long address) { |
| return unsafe.getLong(address); |
| } |
| |
| @Override |
| public void putLong(long address, long value) { |
| unsafe.putLong(address, value); |
| } |
| |
| @Override |
| public byte getByte(Object target, long offset) { |
| return unsafe.getByte(target, offset); |
| } |
| |
| @Override |
| public void putByte(Object target, long offset, byte value) { |
| unsafe.putByte(target, offset, value); |
| } |
| |
| @Override |
| public boolean getBoolean(Object target, long offset) { |
| return unsafe.getBoolean(target, offset); |
| } |
| |
| @Override |
| public void putBoolean(Object target, long offset, boolean value) { |
| unsafe.putBoolean(target, offset, value); |
| } |
| |
| @Override |
| public float getFloat(Object target, long offset) { |
| return unsafe.getFloat(target, offset); |
| } |
| |
| @Override |
| public void putFloat(Object target, long offset, float value) { |
| unsafe.putFloat(target, offset, value); |
| } |
| |
| @Override |
| public double getDouble(Object target, long offset) { |
| return unsafe.getDouble(target, offset); |
| } |
| |
| @Override |
| public void putDouble(Object target, long offset, double value) { |
| unsafe.putDouble(target, offset, value); |
| } |
| |
| @Override |
| public void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) { |
| unsafe.copyMemory(null, srcOffset, target, BYTE_ARRAY_BASE_OFFSET + targetIndex, length); |
| } |
| |
| @Override |
| public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) { |
| unsafe.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, targetOffset, length); |
| } |
| |
| @Override |
| public Object getStaticObject(Field field) { |
| return getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field)); |
| } |
| } |
| |
| } |