/*
 * Copyright 2012 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 org.jctools.queues.MpscArrayQueue;
import org.jctools.queues.MpscChunkedArrayQueue;
import org.jctools.queues.MpscUnboundedArrayQueue;
import org.jctools.queues.SpscLinkedQueue;
import org.jctools.queues.atomic.MpscAtomicArrayQueue;
import org.jctools.queues.atomic.MpscGrowableAtomicArrayQueue;
import org.jctools.queues.atomic.MpscUnboundedAtomicArrayQueue;
import org.jctools.queues.atomic.SpscLinkedAtomicQueue;
import org.jctools.util.Pow2;
import org.jctools.util.UnsafeAccess;

import java.io.File;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Deque;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static io.netty.util.internal.PlatformDependent0.HASH_CODE_ASCII_SEED;
import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C1;
import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C2;
import static io.netty.util.internal.PlatformDependent0.hashCodeAsciiSanitize;
import static io.netty.util.internal.PlatformDependent0.unalignedAccess;
import static java.lang.Math.max;
import static java.lang.Math.min;

Utility that detects various properties specific to the current runtime environment, such as Java version and the availability of the sun.misc.Unsafe object.

You can disable the use of sun.misc.Unsafe if you specify the system property io.netty.noUnsafe.

/** * Utility that detects various properties specific to the current runtime * environment, such as Java version and the availability of the * {@code sun.misc.Unsafe} object. * <p> * You can disable the use of {@code sun.misc.Unsafe} if you specify * the system property <strong>io.netty.noUnsafe</strong>. */
public final class PlatformDependent { private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class); private static final Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN = Pattern.compile( "\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$"); private static final boolean IS_WINDOWS = isWindows0(); private static final boolean IS_OSX = isOsx0(); private static final boolean MAYBE_SUPER_USER; private static final boolean CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !isAndroid(); private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause0(); private static final boolean DIRECT_BUFFER_PREFERRED = UNSAFE_UNAVAILABILITY_CAUSE == null && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false); private static final long MAX_DIRECT_MEMORY = maxDirectMemory0(); private static final int MPSC_CHUNK_SIZE = 1024; private static final int MIN_MAX_MPSC_CAPACITY = MPSC_CHUNK_SIZE * 2; private static final int MAX_ALLOWED_MPSC_CAPACITY = Pow2.MAX_POW2; private static final long BYTE_ARRAY_BASE_OFFSET = byteArrayBaseOffset0(); private static final File TMPDIR = tmpdir0(); private static final int BIT_MODE = bitMode0(); private static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", "")); private static final String NORMALIZED_OS = normalizeOs(SystemPropertyUtil.get("os.name", "")); private static final int ADDRESS_SIZE = addressSize0(); private static final boolean USE_DIRECT_BUFFER_NO_CLEANER; private static final AtomicLong DIRECT_MEMORY_COUNTER; private static final long DIRECT_MEMORY_LIMIT; private static final ThreadLocalRandomProvider RANDOM_PROVIDER; private static final Cleaner CLEANER; private static final int UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD; public static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; private static final Cleaner NOOP = new Cleaner() { @Override public void freeDirectBuffer(ByteBuffer buffer) { // NOOP } }; static { if (javaVersion() >= 7) { RANDOM_PROVIDER = new ThreadLocalRandomProvider() { @Override public Random current() { return java.util.concurrent.ThreadLocalRandom.current(); } }; } else { RANDOM_PROVIDER = new ThreadLocalRandomProvider() { @Override public Random current() { return ThreadLocalRandom.current(); } }; } if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); } /* * We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe * guard. */ if (!hasUnsafe() && !isAndroid() && !PlatformDependent0.isExplicitNoUnsafe()) { logger.info( "Your platform does not provide complete low-level API for accessing direct buffers reliably. " + "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + "instability."); } // Here is how the system property is used: // // * < 0 - Don't use cleaner, and inherit max direct memory from java. In this case the // "practical max direct memory" would be 2 * max memory as defined by the JDK. // * == 0 - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK. // * > 0 - Don't use cleaner. This will limit Netty's total direct memory // (note: that JDK's direct memory limit is independent of this). long maxDirectMemory = SystemPropertyUtil.getLong("io.netty.maxDirectMemory", -1); if (maxDirectMemory == 0 || !hasUnsafe() || !PlatformDependent0.hasDirectBufferNoCleanerConstructor()) { USE_DIRECT_BUFFER_NO_CLEANER = false; DIRECT_MEMORY_COUNTER = null; } else { USE_DIRECT_BUFFER_NO_CLEANER = true; if (maxDirectMemory < 0) { maxDirectMemory = maxDirectMemory0(); if (maxDirectMemory <= 0) { DIRECT_MEMORY_COUNTER = null; } else { DIRECT_MEMORY_COUNTER = new AtomicLong(); } } else { DIRECT_MEMORY_COUNTER = new AtomicLong(); } } DIRECT_MEMORY_LIMIT = maxDirectMemory; logger.debug("-Dio.netty.maxDirectMemory: {} bytes", maxDirectMemory); int tryAllocateUninitializedArray = SystemPropertyUtil.getInt("io.netty.uninitializedArrayAllocationThreshold", 1024); UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD = javaVersion() >= 9 && PlatformDependent0.hasAllocateArrayMethod() ? tryAllocateUninitializedArray : -1; logger.debug("-Dio.netty.uninitializedArrayAllocationThreshold: {}", UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD); MAYBE_SUPER_USER = maybeSuperUser0(); if (!isAndroid() && hasUnsafe()) { // only direct to method if we are not running on android. // See https://github.com/netty/netty/issues/2604 if (javaVersion() >= 9) { CLEANER = CleanerJava9.isSupported() ? new CleanerJava9() : NOOP; } else { CLEANER = CleanerJava6.isSupported() ? new CleanerJava6() : NOOP; } } else { CLEANER = NOOP; } } public static boolean hasDirectBufferNoCleanerConstructor() { return PlatformDependent0.hasDirectBufferNoCleanerConstructor(); } public static byte[] allocateUninitializedArray(int size) { return UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD < 0 || UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD > size ? new byte[size] : PlatformDependent0.allocateUninitializedArray(size); }
Returns true if and only if the current platform is Android
/** * Returns {@code true} if and only if the current platform is Android */
public static boolean isAndroid() { return PlatformDependent0.isAndroid(); }
Return true if the JVM is running on Windows
/** * Return {@code true} if the JVM is running on Windows */
public static boolean isWindows() { return IS_WINDOWS; }
Return true if the JVM is running on OSX / MacOS
/** * Return {@code true} if the JVM is running on OSX / MacOS */
public static boolean isOsx() { return IS_OSX; }
Return true if the current user may be a super-user. Be aware that this is just an hint and so it may return false-positives.
/** * Return {@code true} if the current user may be a super-user. Be aware that this is just an hint and so it may * return false-positives. */
public static boolean maybeSuperUser() { return MAYBE_SUPER_USER; }
Return the version of Java under which this library is used.
/** * Return the version of Java under which this library is used. */
public static int javaVersion() { return PlatformDependent0.javaVersion(); }
Returns true if and only if it is fine to enable TCP_NODELAY socket option by default.
/** * Returns {@code true} if and only if it is fine to enable TCP_NODELAY socket option by default. */
public static boolean canEnableTcpNoDelayByDefault() { return CAN_ENABLE_TCP_NODELAY_BY_DEFAULT; }
Return true if sun.misc.Unsafe was found on the classpath and can be used for accelerated direct memory access.
/** * Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used for accelerated * direct memory access. */
public static boolean hasUnsafe() { return UNSAFE_UNAVAILABILITY_CAUSE == null; }
Return the reason (if any) why sun.misc.Unsafe was not available.
/** * Return the reason (if any) why {@code sun.misc.Unsafe} was not available. */
public static Throwable getUnsafeUnavailabilityCause() { return UNSAFE_UNAVAILABILITY_CAUSE; }
true if and only if the platform supports unaligned access.
See Also:
/** * {@code true} if and only if the platform supports unaligned access. * * @see <a href="http://en.wikipedia.org/wiki/Segmentation_fault#Bus_error">Wikipedia on segfault</a> */
public static boolean isUnaligned() { return PlatformDependent0.isUnaligned(); }
Returns true if the platform has reliable low-level direct buffer access API and a user has not specified -Dio.netty.noPreferDirect option.
/** * Returns {@code true} if the platform has reliable low-level direct buffer access API and a user has not specified * {@code -Dio.netty.noPreferDirect} option. */
public static boolean directBufferPreferred() { return DIRECT_BUFFER_PREFERRED; }
Returns the maximum memory reserved for direct buffer allocation.
/** * Returns the maximum memory reserved for direct buffer allocation. */
public static long maxDirectMemory() { return MAX_DIRECT_MEMORY; }
Returns the temporary directory.
/** * Returns the temporary directory. */
public static File tmpdir() { return TMPDIR; }
Returns the bit mode of the current VM (usually 32 or 64.)
/** * Returns the bit mode of the current VM (usually 32 or 64.) */
public static int bitMode() { return BIT_MODE; }
Return the address size of the OS. 4 (for 32 bits systems ) and 8 (for 64 bits systems).
/** * Return the address size of the OS. * 4 (for 32 bits systems ) and 8 (for 64 bits systems). */
public static int addressSize() { return ADDRESS_SIZE; } public static long allocateMemory(long size) { return PlatformDependent0.allocateMemory(size); } public static void freeMemory(long address) { PlatformDependent0.freeMemory(address); } public static long reallocateMemory(long address, long newSize) { return PlatformDependent0.reallocateMemory(address, newSize); }
Raises an exception bypassing compiler checks for checked exceptions.
/** * Raises an exception bypassing compiler checks for checked exceptions. */
public static void throwException(Throwable t) { if (hasUnsafe()) { PlatformDependent0.throwException(t); } else { PlatformDependent.<RuntimeException>throwException0(t); } } @SuppressWarnings("unchecked") private static <E extends Throwable> void throwException0(Throwable t) throws E { throw (E) t; }
Creates a new fastest ConcurrentMap implementation for the current platform.
/** * Creates a new fastest {@link ConcurrentMap} implementation for the current platform. */
public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap() { return new ConcurrentHashMap<K, V>(); }
Creates a new fastest LongCounter implementation for the current platform.
/** * Creates a new fastest {@link LongCounter} implementation for the current platform. */
public static LongCounter newLongCounter() { if (javaVersion() >= 8) { return new LongAdderCounter(); } else { return new AtomicLongCounter(); } }
Creates a new fastest ConcurrentMap implementation for the current platform.
/** * Creates a new fastest {@link ConcurrentMap} implementation for the current platform. */
public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity) { return new ConcurrentHashMap<K, V>(initialCapacity); }
Creates a new fastest ConcurrentMap implementation for the current platform.
/** * Creates a new fastest {@link ConcurrentMap} implementation for the current platform. */
public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity, float loadFactor) { return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor); }
Creates a new fastest ConcurrentMap implementation for the current platform.
/** * Creates a new fastest {@link ConcurrentMap} implementation for the current platform. */
public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap( int initialCapacity, float loadFactor, int concurrencyLevel) { return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor, concurrencyLevel); }
Creates a new fastest ConcurrentMap implementation for the current platform.
/** * Creates a new fastest {@link ConcurrentMap} implementation for the current platform. */
public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(Map<? extends K, ? extends V> map) { return new ConcurrentHashMap<K, V>(map); }
Try to deallocate the specified direct ByteBuffer. Please note this method does nothing if the current platform does not support this operation or the specified buffer is not a direct buffer.
/** * Try to deallocate the specified direct {@link ByteBuffer}. Please note this method does nothing if * the current platform does not support this operation or the specified buffer is not a direct buffer. */
public static void freeDirectBuffer(ByteBuffer buffer) { CLEANER.freeDirectBuffer(buffer); } public static long directBufferAddress(ByteBuffer buffer) { return PlatformDependent0.directBufferAddress(buffer); } public static ByteBuffer directBuffer(long memoryAddress, int size) { if (PlatformDependent0.hasDirectBufferNoCleanerConstructor()) { return PlatformDependent0.newDirectBuffer(memoryAddress, size); } throw new UnsupportedOperationException( "sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not available"); } public static int getInt(Object object, long fieldOffset) { return PlatformDependent0.getInt(object, fieldOffset); } public static byte getByte(long address) { return PlatformDependent0.getByte(address); } public static short getShort(long address) { return PlatformDependent0.getShort(address); } public static int getInt(long address) { return PlatformDependent0.getInt(address); } public static long getLong(long address) { return PlatformDependent0.getLong(address); } public static byte getByte(byte[] data, int index) { return PlatformDependent0.getByte(data, index); } public static short getShort(byte[] data, int index) { return PlatformDependent0.getShort(data, index); } public static int getInt(byte[] data, int index) { return PlatformDependent0.getInt(data, index); } public static long getLong(byte[] data, int index) { return PlatformDependent0.getLong(data, index); } private static long getLongSafe(byte[] bytes, int offset) { if (BIG_ENDIAN_NATIVE_ORDER) { return (long) bytes[offset] << 56 | ((long) bytes[offset + 1] & 0xff) << 48 | ((long) bytes[offset + 2] & 0xff) << 40 | ((long) bytes[offset + 3] & 0xff) << 32 | ((long) bytes[offset + 4] & 0xff) << 24 | ((long) bytes[offset + 5] & 0xff) << 16 | ((long) bytes[offset + 6] & 0xff) << 8 | (long) bytes[offset + 7] & 0xff; } return (long) bytes[offset] & 0xff | ((long) bytes[offset + 1] & 0xff) << 8 | ((long) bytes[offset + 2] & 0xff) << 16 | ((long) bytes[offset + 3] & 0xff) << 24 | ((long) bytes[offset + 4] & 0xff) << 32 | ((long) bytes[offset + 5] & 0xff) << 40 | ((long) bytes[offset + 6] & 0xff) << 48 | (long) bytes[offset + 7] << 56; } private static int getIntSafe(byte[] bytes, int offset) { if (BIG_ENDIAN_NATIVE_ORDER) { return bytes[offset] << 24 | (bytes[offset + 1] & 0xff) << 16 | (bytes[offset + 2] & 0xff) << 8 | bytes[offset + 3] & 0xff; } return bytes[offset] & 0xff | (bytes[offset + 1] & 0xff) << 8 | (bytes[offset + 2] & 0xff) << 16 | bytes[offset + 3] << 24; } private static short getShortSafe(byte[] bytes, int offset) { if (BIG_ENDIAN_NATIVE_ORDER) { return (short) (bytes[offset] << 8 | (bytes[offset + 1] & 0xff)); } return (short) (bytes[offset] & 0xff | (bytes[offset + 1] << 8)); } /** * Identical to {@link PlatformDependent0#hashCodeAsciiCompute(long, int)} but for {@link CharSequence}. */ private static int hashCodeAsciiCompute(CharSequence value, int offset, int hash) { if (BIG_ENDIAN_NATIVE_ORDER) { return hash * HASH_CODE_C1 + // Low order int hashCodeAsciiSanitizeInt(value, offset + 4) * HASH_CODE_C2 + // High order int hashCodeAsciiSanitizeInt(value, offset); } return hash * HASH_CODE_C1 + // Low order int hashCodeAsciiSanitizeInt(value, offset) * HASH_CODE_C2 + // High order int hashCodeAsciiSanitizeInt(value, offset + 4); } /** * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(int)} but for {@link CharSequence}. */ private static int hashCodeAsciiSanitizeInt(CharSequence value, int offset) { if (BIG_ENDIAN_NATIVE_ORDER) { // mimic a unsafe.getInt call on a big endian machine return (value.charAt(offset + 3) & 0x1f) | (value.charAt(offset + 2) & 0x1f) << 8 | (value.charAt(offset + 1) & 0x1f) << 16 | (value.charAt(offset) & 0x1f) << 24; } return (value.charAt(offset + 3) & 0x1f) << 24 | (value.charAt(offset + 2) & 0x1f) << 16 | (value.charAt(offset + 1) & 0x1f) << 8 | (value.charAt(offset) & 0x1f); } /** * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(short)} but for {@link CharSequence}. */ private static int hashCodeAsciiSanitizeShort(CharSequence value, int offset) { if (BIG_ENDIAN_NATIVE_ORDER) { // mimic a unsafe.getShort call on a big endian machine return (value.charAt(offset + 1) & 0x1f) | (value.charAt(offset) & 0x1f) << 8; } return (value.charAt(offset + 1) & 0x1f) << 8 | (value.charAt(offset) & 0x1f); } /** * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(byte)} but for {@link CharSequence}. */ private static int hashCodeAsciiSanitizeByte(char value) { return value & 0x1f; } public static void putByte(long address, byte value) { PlatformDependent0.putByte(address, value); } public static void putShort(long address, short value) { PlatformDependent0.putShort(address, value); } public static void putInt(long address, int value) { PlatformDependent0.putInt(address, value); } public static void putLong(long address, long value) { PlatformDependent0.putLong(address, value); } public static void putByte(byte[] data, int index, byte value) { PlatformDependent0.putByte(data, index, value); } public static void putShort(byte[] data, int index, short value) { PlatformDependent0.putShort(data, index, value); } public static void putInt(byte[] data, int index, int value) { PlatformDependent0.putInt(data, index, value); } public static void putLong(byte[] data, int index, long value) { PlatformDependent0.putLong(data, index, value); } public static void copyMemory(long srcAddr, long dstAddr, long length) { PlatformDependent0.copyMemory(srcAddr, dstAddr, length); } public static void copyMemory(byte[] src, int srcIndex, long dstAddr, long length) { PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length); } public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) { PlatformDependent0.copyMemory(null, srcAddr, dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length); } public static void setMemory(byte[] dst, int dstIndex, long bytes, byte value) { PlatformDependent0.setMemory(dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, bytes, value); } public static void setMemory(long address, long bytes, byte value) { PlatformDependent0.setMemory(address, bytes, value); }
Allocate a new ByteBuffer with the given capacity. ByteBuffers allocated with this method MUST be deallocated via freeDirectNoCleaner(ByteBuffer).
/** * Allocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s allocated with * this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}. */
public static ByteBuffer allocateDirectNoCleaner(int capacity) { assert USE_DIRECT_BUFFER_NO_CLEANER; incrementMemoryCounter(capacity); try { return PlatformDependent0.allocateDirectNoCleaner(capacity); } catch (Throwable e) { decrementMemoryCounter(capacity); throwException(e); return null; } }
Reallocate a new ByteBuffer with the given capacity. ByteBuffers reallocated with this method MUST be deallocated via freeDirectNoCleaner(ByteBuffer).
/** * Reallocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s reallocated with * this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}. */
public static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) { assert USE_DIRECT_BUFFER_NO_CLEANER; int len = capacity - buffer.capacity(); incrementMemoryCounter(len); try { return PlatformDependent0.reallocateDirectNoCleaner(buffer, capacity); } catch (Throwable e) { decrementMemoryCounter(len); throwException(e); return null; } }
This method MUST only be called for ByteBuffers that were allocated via allocateDirectNoCleaner(int).
/** * This method <strong>MUST</strong> only be called for {@link ByteBuffer}s that were allocated via * {@link #allocateDirectNoCleaner(int)}. */
public static void freeDirectNoCleaner(ByteBuffer buffer) { assert USE_DIRECT_BUFFER_NO_CLEANER; int capacity = buffer.capacity(); PlatformDependent0.freeMemory(PlatformDependent0.directBufferAddress(buffer)); decrementMemoryCounter(capacity); } private static void incrementMemoryCounter(int capacity) { if (DIRECT_MEMORY_COUNTER != null) { for (;;) { long usedMemory = DIRECT_MEMORY_COUNTER.get(); long newUsedMemory = usedMemory + capacity; if (newUsedMemory > DIRECT_MEMORY_LIMIT) { throw new OutOfDirectMemoryError("failed to allocate " + capacity + " byte(s) of direct memory (used: " + usedMemory + ", max: " + DIRECT_MEMORY_LIMIT + ')'); } if (DIRECT_MEMORY_COUNTER.compareAndSet(usedMemory, newUsedMemory)) { break; } } } } private static void decrementMemoryCounter(int capacity) { if (DIRECT_MEMORY_COUNTER != null) { long usedMemory = DIRECT_MEMORY_COUNTER.addAndGet(-capacity); assert usedMemory >= 0; } } public static boolean useDirectBufferNoCleaner() { return USE_DIRECT_BUFFER_NO_CLEANER; }
Compare two byte arrays for equality. For performance reasons no bounds checking on the parameters is performed.
Params:
  • bytes1 – the first byte array.
  • startPos1 – the position (inclusive) to start comparing in bytes1.
  • bytes2 – the second byte array.
  • startPos2 – the position (inclusive) to start comparing in bytes2.
  • length – the amount of bytes to compare. This is assumed to be validated as not going out of bounds by the caller.
/** * Compare two {@code byte} arrays for equality. For performance reasons no bounds checking on the * parameters is performed. * * @param bytes1 the first byte array. * @param startPos1 the position (inclusive) to start comparing in {@code bytes1}. * @param bytes2 the second byte array. * @param startPos2 the position (inclusive) to start comparing in {@code bytes2}. * @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds * by the caller. */
public static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { return !hasUnsafe() || !unalignedAccess() ? equalsSafe(bytes1, startPos1, bytes2, startPos2, length) : PlatformDependent0.equals(bytes1, startPos1, bytes2, startPos2, length); }
Determine if a subsection of an array is zero.
Params:
  • bytes – The byte array.
  • startPos – The starting index (inclusive) in bytes.
  • length – The amount of bytes to check for zero.
Returns:false if bytes[startPos:startsPos+length) contains a value other than zero.
/** * Determine if a subsection of an array is zero. * @param bytes The byte array. * @param startPos The starting index (inclusive) in {@code bytes}. * @param length The amount of bytes to check for zero. * @return {@code false} if {@code bytes[startPos:startsPos+length)} contains a value other than zero. */
public static boolean isZero(byte[] bytes, int startPos, int length) { return !hasUnsafe() || !unalignedAccess() ? isZeroSafe(bytes, startPos, length) : PlatformDependent0.isZero(bytes, startPos, length); }
Compare two byte arrays for equality without leaking timing information. For performance reasons no bounds checking on the parameters is performed.

The int return type is intentional and is designed to allow cascading of constant time operations:

    byte[] s1 = new {1, 2, 3};
    byte[] s2 = new {1, 2, 3};
    byte[] s3 = new {1, 2, 3};
    byte[] s4 = new {4, 5, 6};
    boolean equals = (equalsConstantTime(s1, 0, s2, 0, s1.length) &
                      equalsConstantTime(s3, 0, s4, 0, s3.length)) != 0;
Params:
  • bytes1 – the first byte array.
  • startPos1 – the position (inclusive) to start comparing in bytes1.
  • bytes2 – the second byte array.
  • startPos2 – the position (inclusive) to start comparing in bytes2.
  • length – the amount of bytes to compare. This is assumed to be validated as not going out of bounds by the caller.
Returns:0 if not equal. 1 if equal.
/** * Compare two {@code byte} arrays for equality without leaking timing information. * For performance reasons no bounds checking on the parameters is performed. * <p> * The {@code int} return type is intentional and is designed to allow cascading of constant time operations: * <pre> * byte[] s1 = new {1, 2, 3}; * byte[] s2 = new {1, 2, 3}; * byte[] s3 = new {1, 2, 3}; * byte[] s4 = new {4, 5, 6}; * boolean equals = (equalsConstantTime(s1, 0, s2, 0, s1.length) & * equalsConstantTime(s3, 0, s4, 0, s3.length)) != 0; * </pre> * @param bytes1 the first byte array. * @param startPos1 the position (inclusive) to start comparing in {@code bytes1}. * @param bytes2 the second byte array. * @param startPos2 the position (inclusive) to start comparing in {@code bytes2}. * @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds * by the caller. * @return {@code 0} if not equal. {@code 1} if equal. */
public static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { return !hasUnsafe() || !unalignedAccess() ? ConstantTimeUtils.equalsConstantTime(bytes1, startPos1, bytes2, startPos2, length) : PlatformDependent0.equalsConstantTime(bytes1, startPos1, bytes2, startPos2, length); }
Calculate a hash code of a byte array assuming ASCII character encoding. The resulting hash code will be case insensitive.
Params:
  • bytes – The array which contains the data to hash.
  • startPos – What index to start generating a hash code in bytes
  • length – The amount of bytes that should be accounted for in the computation.
Returns:The hash code of bytes assuming ASCII character encoding. The resulting hash code will be case insensitive.
/** * Calculate a hash code of a byte array assuming ASCII character encoding. * The resulting hash code will be case insensitive. * @param bytes The array which contains the data to hash. * @param startPos What index to start generating a hash code in {@code bytes} * @param length The amount of bytes that should be accounted for in the computation. * @return The hash code of {@code bytes} assuming ASCII character encoding. * The resulting hash code will be case insensitive. */
public static int hashCodeAscii(byte[] bytes, int startPos, int length) { return !hasUnsafe() || !unalignedAccess() ? hashCodeAsciiSafe(bytes, startPos, length) : PlatformDependent0.hashCodeAscii(bytes, startPos, length); }
Calculate a hash code of a byte array assuming ASCII character encoding. The resulting hash code will be case insensitive.

This method assumes that bytes is equivalent to a byte[] but just using CharSequence for storage. The upper most byte of each char from bytes is ignored.

Params:
  • bytes – The array which contains the data to hash (assumed to be equivalent to a byte[]).
Returns:The hash code of bytes assuming ASCII character encoding. The resulting hash code will be case insensitive.
/** * Calculate a hash code of a byte array assuming ASCII character encoding. * The resulting hash code will be case insensitive. * <p> * This method assumes that {@code bytes} is equivalent to a {@code byte[]} but just using {@link CharSequence} * for storage. The upper most byte of each {@code char} from {@code bytes} is ignored. * @param bytes The array which contains the data to hash (assumed to be equivalent to a {@code byte[]}). * @return The hash code of {@code bytes} assuming ASCII character encoding. * The resulting hash code will be case insensitive. */
public static int hashCodeAscii(CharSequence bytes) { int hash = HASH_CODE_ASCII_SEED; final int remainingBytes = bytes.length() & 7; // Benchmarking shows that by just naively looping for inputs 8~31 bytes long we incur a relatively large // performance penalty (only achieve about 60% performance of loop which iterates over each char). So because // of this we take special provisions to unroll the looping for these conditions. switch (bytes.length()) { case 31: case 30: case 29: case 28: case 27: case 26: case 25: case 24: hash = hashCodeAsciiCompute(bytes, bytes.length() - 24, hashCodeAsciiCompute(bytes, bytes.length() - 16, hashCodeAsciiCompute(bytes, bytes.length() - 8, hash))); break; case 23: case 22: case 21: case 20: case 19: case 18: case 17: case 16: hash = hashCodeAsciiCompute(bytes, bytes.length() - 16, hashCodeAsciiCompute(bytes, bytes.length() - 8, hash)); break; case 15: case 14: case 13: case 12: case 11: case 10: case 9: case 8: hash = hashCodeAsciiCompute(bytes, bytes.length() - 8, hash); break; case 7: case 6: case 5: case 4: case 3: case 2: case 1: case 0: break; default: for (int i = bytes.length() - 8; i >= remainingBytes; i -= 8) { hash = hashCodeAsciiCompute(bytes, i, hash); } break; } switch(remainingBytes) { case 7: return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) * HASH_CODE_C2 + hashCodeAsciiSanitizeShort(bytes, 1)) * HASH_CODE_C1 + hashCodeAsciiSanitizeInt(bytes, 3); case 6: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeShort(bytes, 0)) * HASH_CODE_C2 + hashCodeAsciiSanitizeInt(bytes, 2); case 5: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) * HASH_CODE_C2 + hashCodeAsciiSanitizeInt(bytes, 1); case 4: return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeInt(bytes, 0); case 3: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) * HASH_CODE_C2 + hashCodeAsciiSanitizeShort(bytes, 1); case 2: return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeShort(bytes, 0); case 1: return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0)); default: return hash; } } private static final class Mpsc { private static final boolean USE_MPSC_CHUNKED_ARRAY_QUEUE; private Mpsc() { } static { Object unsafe = null; if (hasUnsafe()) { // jctools goes through its own process of initializing unsafe; of // course, this requires permissions which might not be granted to calling code, so we // must mark this block as privileged too unsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { // force JCTools to initialize unsafe return UnsafeAccess.UNSAFE; } }); } if (unsafe == null) { logger.debug("org.jctools-core.MpscChunkedArrayQueue: unavailable"); USE_MPSC_CHUNKED_ARRAY_QUEUE = false; } else { logger.debug("org.jctools-core.MpscChunkedArrayQueue: available"); USE_MPSC_CHUNKED_ARRAY_QUEUE = true; } } static <T> Queue<T> newMpscQueue(final int maxCapacity) { // Calculate the max capacity which can not be bigger then MAX_ALLOWED_MPSC_CAPACITY. // This is forced by the MpscChunkedArrayQueue implementation as will try to round it // up to the next power of two and so will overflow otherwise. final int capacity = max(min(maxCapacity, MAX_ALLOWED_MPSC_CAPACITY), MIN_MAX_MPSC_CAPACITY); return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscChunkedArrayQueue<T>(MPSC_CHUNK_SIZE, capacity) : new MpscGrowableAtomicArrayQueue<T>(MPSC_CHUNK_SIZE, capacity); } static <T> Queue<T> newMpscQueue() { return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscUnboundedArrayQueue<T>(MPSC_CHUNK_SIZE) : new MpscUnboundedAtomicArrayQueue<T>(MPSC_CHUNK_SIZE); } }
Create a new Queue which is safe to use for multiple producers (different threads) and a single consumer (one thread!).
Returns:A MPSC queue which may be unbounded.
/** * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single * consumer (one thread!). * @return A MPSC queue which may be unbounded. */
public static <T> Queue<T> newMpscQueue() { return Mpsc.newMpscQueue(); }
Create a new Queue which is safe to use for multiple producers (different threads) and a single consumer (one thread!).
/** * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single * consumer (one thread!). */
public static <T> Queue<T> newMpscQueue(final int maxCapacity) { return Mpsc.newMpscQueue(maxCapacity); }
Create a new Queue which is safe to use for single producer (one thread!) and a single consumer (one thread!).
/** * Create a new {@link Queue} which is safe to use for single producer (one thread!) and a single * consumer (one thread!). */
public static <T> Queue<T> newSpscQueue() { return hasUnsafe() ? new SpscLinkedQueue<T>() : new SpscLinkedAtomicQueue<T>(); }
Create a new Queue which is safe to use for multiple producers (different threads) and a single consumer (one thread!) with the given fixes capacity.
/** * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single * consumer (one thread!) with the given fixes {@code capacity}. */
public static <T> Queue<T> newFixedMpscQueue(int capacity) { return hasUnsafe() ? new MpscArrayQueue<T>(capacity) : new MpscAtomicArrayQueue<T>(capacity); }
Return the ClassLoader for the given Class.
/** * Return the {@link ClassLoader} for the given {@link Class}. */
public static ClassLoader getClassLoader(final Class<?> clazz) { return PlatformDependent0.getClassLoader(clazz); }
Return the context ClassLoader for the current Thread.
/** * Return the context {@link ClassLoader} for the current {@link Thread}. */
public static ClassLoader getContextClassLoader() { return PlatformDependent0.getContextClassLoader(); }
Return the system ClassLoader.
/** * Return the system {@link ClassLoader}. */
public static ClassLoader getSystemClassLoader() { return PlatformDependent0.getSystemClassLoader(); }
Returns a new concurrent Deque.
/** * Returns a new concurrent {@link Deque}. */
public static <C> Deque<C> newConcurrentDeque() { if (javaVersion() < 7) { return new LinkedBlockingDeque<C>(); } else { return new ConcurrentLinkedDeque<C>(); } }
Return a Random which is not-threadsafe and so can only be used from the same thread.
/** * Return a {@link Random} which is not-threadsafe and so can only be used from the same thread. */
public static Random threadLocalRandom() { return RANDOM_PROVIDER.current(); } private static boolean isWindows0() { boolean windows = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US).contains("win"); if (windows) { logger.debug("Platform: Windows"); } return windows; } private static boolean isOsx0() { String osname = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US) .replaceAll("[^a-z0-9]+", ""); boolean osx = osname.startsWith("macosx") || osname.startsWith("osx"); if (osx) { logger.debug("Platform: MacOS"); } return osx; } private static boolean maybeSuperUser0() { String username = SystemPropertyUtil.get("user.name"); if (isWindows()) { return "Administrator".equals(username); } // Check for root and toor as some BSDs have a toor user that is basically the same as root. return "root".equals(username) || "toor".equals(username); } private static Throwable unsafeUnavailabilityCause0() { if (isAndroid()) { logger.debug("sun.misc.Unsafe: unavailable (Android)"); return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (Android)"); } Throwable cause = PlatformDependent0.getUnsafeUnavailabilityCause(); if (cause != null) { return cause; } try { boolean hasUnsafe = PlatformDependent0.hasUnsafe(); logger.debug("sun.misc.Unsafe: {}", hasUnsafe ? "available" : "unavailable"); return hasUnsafe ? null : PlatformDependent0.getUnsafeUnavailabilityCause(); } catch (Throwable t) { logger.trace("Could not determine if Unsafe is available", t); // Probably failed to initialize PlatformDependent0. return new UnsupportedOperationException("Could not determine if Unsafe is available", t); } } private static long maxDirectMemory0() { long maxDirectMemory = 0; ClassLoader systemClassLoader = null; try { systemClassLoader = getSystemClassLoader(); // On z/OS we should not use VM.maxDirectMemory() as it not reflects the correct value. // See: // - https://github.com/netty/netty/issues/7654 if (!SystemPropertyUtil.get("os.name", "").toLowerCase().contains("z/os")) { // Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate. Class<?> vmClass = Class.forName("sun.misc.VM", true, systemClassLoader); Method m = vmClass.getDeclaredMethod("maxDirectMemory"); maxDirectMemory = ((Number) m.invoke(null)).longValue(); } } catch (Throwable ignored) { // Ignore } if (maxDirectMemory > 0) { return maxDirectMemory; } try { // Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it. // Note that we are using reflection because Android doesn't have these classes. Class<?> mgmtFactoryClass = Class.forName( "java.lang.management.ManagementFactory", true, systemClassLoader); Class<?> runtimeClass = Class.forName( "java.lang.management.RuntimeMXBean", true, systemClassLoader); Object runtime = mgmtFactoryClass.getDeclaredMethod("getRuntimeMXBean").invoke(null); @SuppressWarnings("unchecked") List<String> vmArgs = (List<String>) runtimeClass.getDeclaredMethod("getInputArguments").invoke(runtime); for (int i = vmArgs.size() - 1; i >= 0; i --) { Matcher m = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.matcher(vmArgs.get(i)); if (!m.matches()) { continue; } maxDirectMemory = Long.parseLong(m.group(1)); switch (m.group(2).charAt(0)) { case 'k': case 'K': maxDirectMemory *= 1024; break; case 'm': case 'M': maxDirectMemory *= 1024 * 1024; break; case 'g': case 'G': maxDirectMemory *= 1024 * 1024 * 1024; break; } break; } } catch (Throwable ignored) { // Ignore } if (maxDirectMemory <= 0) { maxDirectMemory = Runtime.getRuntime().maxMemory(); logger.debug("maxDirectMemory: {} bytes (maybe)", maxDirectMemory); } else { logger.debug("maxDirectMemory: {} bytes", maxDirectMemory); } return maxDirectMemory; } private static File tmpdir0() { File f; try { f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir")); if (f != null) { logger.debug("-Dio.netty.tmpdir: {}", f); return f; } f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir")); if (f != null) { logger.debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)", f); return f; } // This shouldn't happen, but just in case .. if (isWindows()) { f = toDirectory(System.getenv("TEMP")); if (f != null) { logger.debug("-Dio.netty.tmpdir: {} (%TEMP%)", f); return f; } String userprofile = System.getenv("USERPROFILE"); if (userprofile != null) { f = toDirectory(userprofile + "\\AppData\\Local\\Temp"); if (f != null) { logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f); return f; } f = toDirectory(userprofile + "\\Local Settings\\Temp"); if (f != null) { logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f); return f; } } } else { f = toDirectory(System.getenv("TMPDIR")); if (f != null) { logger.debug("-Dio.netty.tmpdir: {} ($TMPDIR)", f); return f; } } } catch (Throwable ignored) { // Environment variable inaccessible } // Last resort. if (isWindows()) { f = new File("C:\\Windows\\Temp"); } else { f = new File("/tmp"); } logger.warn("Failed to get the temporary directory; falling back to: {}", f); return f; } @SuppressWarnings("ResultOfMethodCallIgnored") private static File toDirectory(String path) { if (path == null) { return null; } File f = new File(path); f.mkdirs(); if (!f.isDirectory()) { return null; } try { return f.getAbsoluteFile(); } catch (Exception ignored) { return f; } } private static int bitMode0() { // Check user-specified bit mode first. int bitMode = SystemPropertyUtil.getInt("io.netty.bitMode", 0); if (bitMode > 0) { logger.debug("-Dio.netty.bitMode: {}", bitMode); return bitMode; } // And then the vendor specific ones which is probably most reliable. bitMode = SystemPropertyUtil.getInt("sun.arch.data.model", 0); if (bitMode > 0) { logger.debug("-Dio.netty.bitMode: {} (sun.arch.data.model)", bitMode); return bitMode; } bitMode = SystemPropertyUtil.getInt("com.ibm.vm.bitmode", 0); if (bitMode > 0) { logger.debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)", bitMode); return bitMode; } // os.arch also gives us a good hint. String arch = SystemPropertyUtil.get("os.arch", "").toLowerCase(Locale.US).trim(); if ("amd64".equals(arch) || "x86_64".equals(arch)) { bitMode = 64; } else if ("i386".equals(arch) || "i486".equals(arch) || "i586".equals(arch) || "i686".equals(arch)) { bitMode = 32; } if (bitMode > 0) { logger.debug("-Dio.netty.bitMode: {} (os.arch: {})", bitMode, arch); } // Last resort: guess from VM name and then fall back to most common 64-bit mode. String vm = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(Locale.US); Pattern BIT_PATTERN = Pattern.compile("([1-9][0-9]+)-?bit"); Matcher m = BIT_PATTERN.matcher(vm); if (m.find()) { return Integer.parseInt(m.group(1)); } else { return 64; } } private static int addressSize0() { if (!hasUnsafe()) { return -1; } return PlatformDependent0.addressSize(); } private static long byteArrayBaseOffset0() { if (!hasUnsafe()) { return -1; } return PlatformDependent0.byteArrayBaseOffset(); } private static boolean equalsSafe(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { final int end = startPos1 + length; for (; startPos1 < end; ++startPos1, ++startPos2) { if (bytes1[startPos1] != bytes2[startPos2]) { return false; } } return true; } private static boolean isZeroSafe(byte[] bytes, int startPos, int length) { final int end = startPos + length; for (; startPos < end; ++startPos) { if (bytes[startPos] != 0) { return false; } } return true; }
Package private for testing purposes only!
/** * Package private for testing purposes only! */
static int hashCodeAsciiSafe(byte[] bytes, int startPos, int length) { int hash = HASH_CODE_ASCII_SEED; final int remainingBytes = length & 7; final int end = startPos + remainingBytes; for (int i = startPos - 8 + length; i >= end; i -= 8) { hash = PlatformDependent0.hashCodeAsciiCompute(getLongSafe(bytes, i), hash); } switch(remainingBytes) { case 7: return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos])) * HASH_CODE_C2 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos + 1))) * HASH_CODE_C1 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 3)); case 6: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos))) * HASH_CODE_C2 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 2)); case 5: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos])) * HASH_CODE_C2 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 1)); case 4: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos)); case 3: return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos])) * HASH_CODE_C2 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos + 1)); case 2: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos)); case 1: return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos]); default: return hash; } } public static String normalizedArch() { return NORMALIZED_ARCH; } public static String normalizedOs() { return NORMALIZED_OS; } private static String normalize(String value) { return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); } private static String normalizeArch(String value) { value = normalize(value); if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) { return "x86_64"; } if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) { return "x86_32"; } if (value.matches("^(ia64|itanium64)$")) { return "itanium_64"; } if (value.matches("^(sparc|sparc32)$")) { return "sparc_32"; } if (value.matches("^(sparcv9|sparc64)$")) { return "sparc_64"; } if (value.matches("^(arm|arm32)$")) { return "arm_32"; } if ("aarch64".equals(value)) { return "aarch_64"; } if (value.matches("^(ppc|ppc32)$")) { return "ppc_32"; } if ("ppc64".equals(value)) { return "ppc_64"; } if ("ppc64le".equals(value)) { return "ppcle_64"; } if ("s390".equals(value)) { return "s390_32"; } if ("s390x".equals(value)) { return "s390_64"; } return "unknown"; } private static String normalizeOs(String value) { value = normalize(value); if (value.startsWith("aix")) { return "aix"; } if (value.startsWith("hpux")) { return "hpux"; } if (value.startsWith("os400")) { // Avoid the names such as os4000 if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) { return "os400"; } } if (value.startsWith("linux")) { return "linux"; } if (value.startsWith("macosx") || value.startsWith("osx")) { return "osx"; } if (value.startsWith("freebsd")) { return "freebsd"; } if (value.startsWith("openbsd")) { return "openbsd"; } if (value.startsWith("netbsd")) { return "netbsd"; } if (value.startsWith("solaris") || value.startsWith("sunos")) { return "sunos"; } if (value.startsWith("windows")) { return "windows"; } return "unknown"; } private static final class AtomicLongCounter extends AtomicLong implements LongCounter { private static final long serialVersionUID = 4074772784610639305L; @Override public void add(long delta) { addAndGet(delta); } @Override public void increment() { incrementAndGet(); } @Override public void decrement() { decrementAndGet(); } @Override public long value() { return get(); } } private interface ThreadLocalRandomProvider { Random current(); } private PlatformDependent() { // only static method supported } }