1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import sun.misc.Unsafe; 34 35 import java.lang.reflect.Field; 36 import java.nio.Buffer; 37 import java.nio.ByteBuffer; 38 import java.security.AccessController; 39 import java.security.PrivilegedExceptionAction; 40 41 /** 42 * Utility class for working with unsafe operations. 43 */ 44 // TODO(nathanmittler): Add support for Android Memory/MemoryBlock 45 final class UnsafeUtil { 46 private static final sun.misc.Unsafe UNSAFE = getUnsafe(); 47 private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS = 48 supportsUnsafeByteBufferOperations(); 49 private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); 50 private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset(); 51 private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(field(Buffer.class, "address")); 52 UnsafeUtil()53 private UnsafeUtil() { 54 } 55 hasUnsafeArrayOperations()56 static boolean hasUnsafeArrayOperations() { 57 return HAS_UNSAFE_ARRAY_OPERATIONS; 58 } 59 hasUnsafeByteBufferOperations()60 static boolean hasUnsafeByteBufferOperations() { 61 return HAS_UNSAFE_BYTEBUFFER_OPERATIONS; 62 } 63 getArrayBaseOffset()64 static long getArrayBaseOffset() { 65 return ARRAY_BASE_OFFSET; 66 } 67 getByte(byte[] target, long offset)68 static byte getByte(byte[] target, long offset) { 69 return UNSAFE.getByte(target, offset); 70 } 71 putByte(byte[] target, long offset, byte value)72 static void putByte(byte[] target, long offset, byte value) { 73 UNSAFE.putByte(target, offset, value); 74 } 75 copyMemory( byte[] src, long srcOffset, byte[] target, long targetOffset, long length)76 static void copyMemory( 77 byte[] src, long srcOffset, byte[] target, long targetOffset, long length) { 78 UNSAFE.copyMemory(src, srcOffset, target, targetOffset, length); 79 } 80 getLong(byte[] target, long offset)81 static long getLong(byte[] target, long offset) { 82 return UNSAFE.getLong(target, offset); 83 } 84 getByte(long address)85 static byte getByte(long address) { 86 return UNSAFE.getByte(address); 87 } 88 putByte(long address, byte value)89 static void putByte(long address, byte value) { 90 UNSAFE.putByte(address, value); 91 } 92 getLong(long address)93 static long getLong(long address) { 94 return UNSAFE.getLong(address); 95 } 96 copyMemory(long srcAddress, long targetAddress, long length)97 static void copyMemory(long srcAddress, long targetAddress, long length) { 98 UNSAFE.copyMemory(srcAddress, targetAddress, length); 99 } 100 101 /** 102 * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}. 103 */ addressOffset(ByteBuffer buffer)104 static long addressOffset(ByteBuffer buffer) { 105 return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET); 106 } 107 108 /** 109 * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform. 110 */ getUnsafe()111 private static sun.misc.Unsafe getUnsafe() { 112 sun.misc.Unsafe unsafe = null; 113 try { 114 unsafe = 115 AccessController.doPrivileged( 116 new PrivilegedExceptionAction<Unsafe>() { 117 @Override 118 public sun.misc.Unsafe run() throws Exception { 119 Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; 120 121 for (Field f : k.getDeclaredFields()) { 122 f.setAccessible(true); 123 Object x = f.get(null); 124 if (k.isInstance(x)) { 125 return k.cast(x); 126 } 127 } 128 // The sun.misc.Unsafe field does not exist. 129 return null; 130 } 131 }); 132 } catch (Throwable e) { 133 // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError 134 // for Unsafe. 135 } 136 return unsafe; 137 } 138 139 /** 140 * Indicates whether or not unsafe array operations are supported on this platform. 141 */ supportsUnsafeArrayOperations()142 private static boolean supportsUnsafeArrayOperations() { 143 boolean supported = false; 144 if (UNSAFE != null) { 145 try { 146 Class<?> clazz = UNSAFE.getClass(); 147 clazz.getMethod("arrayBaseOffset", Class.class); 148 clazz.getMethod("getByte", Object.class, long.class); 149 clazz.getMethod("putByte", Object.class, long.class, byte.class); 150 clazz.getMethod("getLong", Object.class, long.class); 151 clazz.getMethod( 152 "copyMemory", Object.class, long.class, Object.class, long.class, long.class); 153 supported = true; 154 } catch (Throwable e) { 155 // Do nothing. 156 } 157 } 158 return supported; 159 } 160 supportsUnsafeByteBufferOperations()161 private static boolean supportsUnsafeByteBufferOperations() { 162 boolean supported = false; 163 if (UNSAFE != null) { 164 try { 165 Class<?> clazz = UNSAFE.getClass(); 166 clazz.getMethod("objectFieldOffset", Field.class); 167 clazz.getMethod("getByte", long.class); 168 clazz.getMethod("getLong", Object.class, long.class); 169 clazz.getMethod("putByte", long.class, byte.class); 170 clazz.getMethod("getLong", long.class); 171 clazz.getMethod("copyMemory", long.class, long.class, long.class); 172 supported = true; 173 } catch (Throwable e) { 174 // Do nothing. 175 } 176 } 177 return supported; 178 } 179 180 /** 181 * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not available. 182 */ byteArrayBaseOffset()183 private static int byteArrayBaseOffset() { 184 return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1; 185 } 186 187 /** 188 * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not 189 * available. 190 */ fieldOffset(Field field)191 private static long fieldOffset(Field field) { 192 return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field); 193 } 194 195 /** 196 * Gets the field with the given name within the class, or {@code null} if not found. If found, 197 * the field is made accessible. 198 */ field(Class<?> clazz, String fieldName)199 private static Field field(Class<?> clazz, String fieldName) { 200 Field field; 201 try { 202 field = clazz.getDeclaredField(fieldName); 203 field.setAccessible(true); 204 } catch (Throwable t) { 205 // Failed to access the fields. 206 field = null; 207 } 208 return field; 209 } 210 } 211