/*
 * Copyright 2013 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.util.internal;

import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
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.security.AccessController;
import java.security.PrivilegedAction;

import static io.netty.util.internal.ObjectUtil.checkNotNull;

The PlatformDependent operations which requires access to sun.misc.*.
/** * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}. */
final class PlatformDependent0 { private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class); private static final long ADDRESS_FIELD_OFFSET; private static final long BYTE_ARRAY_BASE_OFFSET; private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR; private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0(); private static final Method ALLOCATE_ARRAY_METHOD; private static final int JAVA_VERSION = javaVersion0(); private static final boolean IS_ANDROID = isAndroid0(); private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE; private static final Object INTERNAL_UNSAFE; private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0(); static final Unsafe UNSAFE; // constants borrowed from murmur3 static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35; static final int HASH_CODE_C1 = 0xcc9e2d51; static final int HASH_CODE_C2 = 0x1b873593;
Limits the number of bytes to copy per Unsafe.copyMemory(long, long, long) to allow safepoint polling during a large copy.
/** * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling * during a large copy. */
private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; private static final boolean UNALIGNED; static { final ByteBuffer direct; Field addressField = null; Method allocateArrayMethod = null; Throwable unsafeUnavailabilityCause = null; Unsafe unsafe; Object internalUnsafe = null; if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) { direct = null; addressField = null; unsafe = null; internalUnsafe = null; } else { direct = ByteBuffer.allocateDirect(1); // attempt to access field Unsafe#theUnsafe final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); // We always want to try using Unsafe as the access still works on java9 as well and // we need it for out native-transports and many optimizations. Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false); if (cause != null) { return cause; } // the unsafe instance return unsafeField.get(null); } catch (NoSuchFieldException e) { return e; } catch (SecurityException e) { return e; } catch (IllegalAccessException e) { return e; } catch (NoClassDefFoundError e) { // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made // Unsafe unloadable. return e; } } }); // the conditional check here can not be replaced with checking that maybeUnsafe // is an instanceof Unsafe and reversing the if and else blocks; this is because an // instanceof check against Unsafe will trigger a class load and we might not have // the runtime permission accessClassInPackage.sun.misc if (maybeUnsafe instanceof Throwable) { unsafe = null; unsafeUnavailabilityCause = (Throwable) maybeUnsafe; logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe); } else { unsafe = (Unsafe) maybeUnsafe; logger.debug("sun.misc.Unsafe.theUnsafe: available"); } // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK // https://github.com/netty/netty/issues/1061 // http://www.mail-archive.com/jdk6-dev@openjdk.java.net/msg00698.html if (unsafe != null) { final Unsafe finalUnsafe = unsafe; final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { finalUnsafe.getClass().getDeclaredMethod( "copyMemory", Object.class, long.class, Object.class, long.class, long.class); return null; } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeException == null) { logger.debug("sun.misc.Unsafe.copyMemory: available"); } else { // Unsafe.copyMemory(Object, long, Object, long, long) unavailable. unsafe = null; unsafeUnavailabilityCause = (Throwable) maybeException; logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) maybeException); } } if (unsafe != null) { final Unsafe finalUnsafe = unsafe; // attempt to access field Buffer#address final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Field field = Buffer.class.getDeclaredField("address"); // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which // will forbid changing the access level via reflection. final long offset = finalUnsafe.objectFieldOffset(field); final long address = finalUnsafe.getLong(direct, offset); // if direct really is a direct buffer, address will be non-zero if (address == 0) { return null; } return field; } catch (NoSuchFieldException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeAddressField instanceof Field) { addressField = (Field) maybeAddressField; logger.debug("java.nio.Buffer.address: available"); } else { unsafeUnavailabilityCause = (Throwable) maybeAddressField; logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField); // If we cannot access the address of a direct buffer, there's no point of using unsafe. // Let's just pretend unsafe is unavailable for overall simplicity. unsafe = null; } } if (unsafe != null) { // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case. long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class); if (byteArrayIndexScale != 1) { logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale); unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale"); unsafe = null; } } } UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause; UNSAFE = unsafe; if (unsafe == null) { ADDRESS_FIELD_OFFSET = -1; BYTE_ARRAY_BASE_OFFSET = -1; UNALIGNED = false; DIRECT_BUFFER_CONSTRUCTOR = null; ALLOCATE_ARRAY_METHOD = null; } else { Constructor<?> directBufferConstructor; long address = -1; try { final Object maybeDirectBufferConstructor = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { final Constructor<?> constructor = direct.getClass().getDeclaredConstructor(long.class, int.class); Throwable cause = ReflectionUtil.trySetAccessible(constructor, true); if (cause != null) { return cause; } return constructor; } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeDirectBufferConstructor instanceof Constructor<?>) { address = UNSAFE.allocateMemory(1); // try to use the constructor now try { ((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1); directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor; logger.debug("direct buffer constructor: available"); } catch (InstantiationException e) { directBufferConstructor = null; } catch (IllegalAccessException e) { directBufferConstructor = null; } catch (InvocationTargetException e) { directBufferConstructor = null; } } else { logger.debug( "direct buffer constructor: unavailable", (Throwable) maybeDirectBufferConstructor); directBufferConstructor = null; } } finally { if (address != -1) { UNSAFE.freeMemory(address); } } DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor; ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField); BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); boolean unaligned; Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { Class<?> bitsClass = Class.forName("java.nio.Bits", false, getSystemClassLoader()); Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned"); Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true); if (cause != null) { return cause; } return unalignedMethod.invoke(null); } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } catch (IllegalAccessException e) { return e; } catch (ClassNotFoundException e) { return e; } catch (InvocationTargetException e) { return e; } } }); if (maybeUnaligned instanceof Boolean) { unaligned = (Boolean) maybeUnaligned; logger.debug("java.nio.Bits.unaligned: available, {}", unaligned); } else { String arch = SystemPropertyUtil.get("os.arch", ""); //noinspection DynamicRegexReplaceableByCompiledPattern unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$"); Throwable t = (Throwable) maybeUnaligned; logger.debug("java.nio.Bits.unaligned: unavailable {}", unaligned, t); } UNALIGNED = unaligned; if (javaVersion() >= 9) { Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to // sun.misc.Unsafe Class<?> internalUnsafeClass = getClassLoader(PlatformDependent0.class) .loadClass("jdk.internal.misc.Unsafe"); Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe"); return method.invoke(null); } catch (Throwable e) { return e; } } }); if (!(maybeException instanceof Throwable)) { internalUnsafe = maybeException; final Object finalInternalUnsafe = internalUnsafe; maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { return finalInternalUnsafe.getClass().getDeclaredMethod( "allocateUninitializedArray", Class.class, int.class); } catch (NoSuchMethodException e) { return e; } catch (SecurityException e) { return e; } } }); if (maybeException instanceof Method) { try { Method m = (Method) maybeException; byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8); assert bytes.length == 8; allocateArrayMethod = m; } catch (IllegalAccessException e) { maybeException = e; } catch (InvocationTargetException e) { maybeException = e; } } } if (maybeException instanceof Throwable) { logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable", (Throwable) maybeException); } else { logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available"); } } else { logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9"); } ALLOCATE_ARRAY_METHOD = allocateArrayMethod; } INTERNAL_UNSAFE = internalUnsafe; logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}", DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable"); } static boolean isExplicitNoUnsafe() { return EXPLICIT_NO_UNSAFE_CAUSE == null; } private static Throwable explicitNoUnsafeCause0() { final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false); logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe); if (noUnsafe) { logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)"); return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)"); } // Legacy properties String unsafePropName; if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) { unsafePropName = "io.netty.tryUnsafe"; } else { unsafePropName = "org.jboss.netty.tryUnsafe"; } if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) { String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ")"; logger.debug(msg); return new UnsupportedOperationException(msg); } return null; } static boolean isUnaligned() { return UNALIGNED; } static boolean hasUnsafe() { return UNSAFE != null; } static Throwable getUnsafeUnavailabilityCause() { return UNSAFE_UNAVAILABILITY_CAUSE; } static boolean unalignedAccess() { return UNALIGNED; } static void throwException(Throwable cause) { // JVM has been observed to crash when passing a null argument. See https://github.com/netty/netty/issues/4131. UNSAFE.throwException(checkNotNull(cause, "cause")); } static boolean hasDirectBufferNoCleanerConstructor() { return DIRECT_BUFFER_CONSTRUCTOR != null; } static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) { return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity); } static ByteBuffer allocateDirectNoCleaner(int capacity) { return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity); } static boolean hasAllocateArrayMethod() { return ALLOCATE_ARRAY_METHOD != null; } static byte[] allocateUninitializedArray(int size) { try { return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size); } catch (IllegalAccessException e) { throw new Error(e); } catch (InvocationTargetException e) { throw new Error(e); } } static ByteBuffer newDirectBuffer(long address, int capacity) { ObjectUtil.checkPositiveOrZero(capacity, "capacity"); try { return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity); } catch (Throwable cause) { // Not expected to ever throw! if (cause instanceof Error) { throw (Error) cause; } throw new Error(cause); } } static long directBufferAddress(ByteBuffer buffer) { return getLong(buffer, ADDRESS_FIELD_OFFSET); } static long byteArrayBaseOffset() { return BYTE_ARRAY_BASE_OFFSET; } static Object getObject(Object object, long fieldOffset) { return UNSAFE.getObject(object, fieldOffset); } static int getInt(Object object, long fieldOffset) { return UNSAFE.getInt(object, fieldOffset); } private static long getLong(Object object, long fieldOffset) { return UNSAFE.getLong(object, fieldOffset); } static long objectFieldOffset(Field field) { return UNSAFE.objectFieldOffset(field); } static byte getByte(long address) { return UNSAFE.getByte(address); } static short getShort(long address) { return UNSAFE.getShort(address); } static int getInt(long address) { return UNSAFE.getInt(address); } static long getLong(long address) { return UNSAFE.getLong(address); } static byte getByte(byte[] data, int index) { return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index); } static short getShort(byte[] data, int index) { return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index); } static int getInt(byte[] data, int index) { return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index); } static long getLong(byte[] data, int index) { return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index); } static void putByte(long address, byte value) { UNSAFE.putByte(address, value); } static void putShort(long address, short value) { UNSAFE.putShort(address, value); } static void putInt(long address, int value) { UNSAFE.putInt(address, value); } static void putLong(long address, long value) { UNSAFE.putLong(address, value); } static void putByte(byte[] data, int index, byte value) { UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putShort(byte[] data, int index, short value) { UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putInt(byte[] data, int index, int value) { UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void putLong(byte[] data, int index, long value) { UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value); } static void copyMemory(long srcAddr, long dstAddr, long length) { //UNSAFE.copyMemory(srcAddr, dstAddr, length); while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(srcAddr, dstAddr, size); length -= size; srcAddr += size; dstAddr += size; } } static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) { //UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length); while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size); length -= size; srcOffset += size; dstOffset += size; } } static void setMemory(long address, long bytes, byte value) { UNSAFE.setMemory(address, bytes, value); } static void setMemory(Object o, long offset, long bytes, byte value) { UNSAFE.setMemory(o, offset, bytes, value); } static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { if (length <= 0) { return true; } final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1; final long baseOffset2 = BYTE_ARRAY_BASE_OFFSET + startPos2; int remainingBytes = length & 7; final long end = baseOffset1 + remainingBytes; for (long i = baseOffset1 - 8 + length, j = baseOffset2 - 8 + length; i >= end; i -= 8, j -= 8) { if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, j)) { return false; } } if (remainingBytes >= 4) { remainingBytes -= 4; if (UNSAFE.getInt(bytes1, baseOffset1 + remainingBytes) != UNSAFE.getInt(bytes2, baseOffset2 + remainingBytes)) { return false; } } if (remainingBytes >= 2) { return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) && (remainingBytes == 2 || bytes1[startPos1 + 2] == bytes2[startPos2 + 2]); } return bytes1[startPos1] == bytes2[startPos2]; } static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { long result = 0; final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1; final long baseOffset2 = BYTE_ARRAY_BASE_OFFSET + startPos2; final int remainingBytes = length & 7; final long end = baseOffset1 + remainingBytes; for (long i = baseOffset1 - 8 + length, j = baseOffset2 - 8 + length; i >= end; i -= 8, j -= 8) { result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, j); } switch (remainingBytes) { case 7: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getInt(bytes1, baseOffset1 + 3) ^ UNSAFE.getInt(bytes2, baseOffset2 + 3)) | (UNSAFE.getChar(bytes1, baseOffset1 + 1) ^ UNSAFE.getChar(bytes2, baseOffset2 + 1)) | (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); case 6: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getInt(bytes1, baseOffset1 + 2) ^ UNSAFE.getInt(bytes2, baseOffset2 + 2)) | (UNSAFE.getChar(bytes1, baseOffset1) ^ UNSAFE.getChar(bytes2, baseOffset2)), 0); case 5: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getInt(bytes1, baseOffset1 + 1) ^ UNSAFE.getInt(bytes2, baseOffset2 + 1)) | (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); case 4: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset2)), 0); case 3: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getChar(bytes1, baseOffset1 + 1) ^ UNSAFE.getChar(bytes2, baseOffset2 + 1)) | (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); case 2: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getChar(bytes1, baseOffset1) ^ UNSAFE.getChar(bytes2, baseOffset2)), 0); case 1: return ConstantTimeUtils.equalsConstantTime(result | (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); default: return ConstantTimeUtils.equalsConstantTime(result, 0); } } static boolean isZero(byte[] bytes, int startPos, int length) { if (length <= 0) { return true; } final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos; int remainingBytes = length & 7; final long end = baseOffset + remainingBytes; for (long i = baseOffset - 8 + length; i >= end; i -= 8) { if (UNSAFE.getLong(bytes, i) != 0) { return false; } } if (remainingBytes >= 4) { remainingBytes -= 4; if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) { return false; } } if (remainingBytes >= 2) { return UNSAFE.getChar(bytes, baseOffset) == 0 && (remainingBytes == 2 || bytes[startPos + 2] == 0); } return bytes[startPos] == 0; } static int hashCodeAscii(byte[] bytes, int startPos, int length) { int hash = HASH_CODE_ASCII_SEED; final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos; final int remainingBytes = length & 7; final long end = baseOffset + remainingBytes; for (long i = baseOffset - 8 + length; i >= end; i -= 8) { hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash); } switch(remainingBytes) { case 7: return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset + 1))) * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 3)); case 6: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset))) * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 2)); case 5: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 1)); case 4: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset)); case 3: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset + 1)); case 2: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset)); case 1: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset)); default: return hash; } } static int hashCodeAsciiCompute(long value, int hash) { // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash // code the same regardless of character case (upper case or lower case hash is the same). return hash * HASH_CODE_C1 + // Low order int hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 + // High order int (int) ((value & 0x1f1f1f1f00000000L) >>> 32); } static int hashCodeAsciiSanitize(int value) { return value & 0x1f1f1f1f; } static int hashCodeAsciiSanitize(short value) { return value & 0x1f1f; } static int hashCodeAsciiSanitize(byte value) { return value & 0x1f; } static ClassLoader getClassLoader(final Class<?> clazz) { if (System.getSecurityManager() == null) { return clazz.getClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return clazz.getClassLoader(); } }); } } static ClassLoader getContextClassLoader() { if (System.getSecurityManager() == null) { return Thread.currentThread().getContextClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); } } static ClassLoader getSystemClassLoader() { if (System.getSecurityManager() == null) { return ClassLoader.getSystemClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return ClassLoader.getSystemClassLoader(); } }); } } static int addressSize() { return UNSAFE.addressSize(); } static long allocateMemory(long size) { return UNSAFE.allocateMemory(size); } static void freeMemory(long address) { UNSAFE.freeMemory(address); } static long reallocateMemory(long address, long newSize) { return UNSAFE.reallocateMemory(address, newSize); } static boolean isAndroid() { return IS_ANDROID; } private static boolean isAndroid0() { // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android. // Rather than check if certain classes are present, just check the VM, which is tied to the JDK. // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the // OpenJDK is used, which means `Unsafe` will actually work as expected. // Android sets this property to Dalvik, regardless of whether it actually is. String vmName = SystemPropertyUtil.get("java.vm.name"); boolean isAndroid = "Dalvik".equals(vmName); if (isAndroid) { logger.debug("Platform: Android"); } return isAndroid; } private static boolean explicitTryReflectionSetAccessible0() { // we disable reflective access return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible", javaVersion() < 9); } static boolean isExplicitTryReflectionSetAccessible() { return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE; } static int javaVersion() { return JAVA_VERSION; } private static int javaVersion0() { final int majorVersion; if (isAndroid0()) { majorVersion = 6; } else { majorVersion = majorVersionFromJavaSpecificationVersion(); } logger.debug("Java version: {}", majorVersion); return majorVersion; } // Package-private for testing only static int majorVersionFromJavaSpecificationVersion() { return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6")); } // Package-private for testing only static int majorVersion(final String javaSpecVersion) { final String[] components = javaSpecVersion.split("\\."); final int[] version = new int[components.length]; for (int i = 0; i < components.length; i++) { version[i] = Integer.parseInt(components[i]); } if (version[0] == 1) { assert version[1] >= 6; return version[1]; } else { return version[0]; } } private PlatformDependent0() { } }