View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import org.jctools.queues.MpmcArrayQueue;
21  import org.jctools.queues.MpscArrayQueue;
22  import org.jctools.queues.MpscChunkedArrayQueue;
23  import org.jctools.queues.MpscUnboundedArrayQueue;
24  import org.jctools.queues.SpscLinkedQueue;
25  import org.jctools.queues.atomic.MpmcAtomicArrayQueue;
26  import org.jctools.queues.atomic.MpscAtomicArrayQueue;
27  import org.jctools.queues.atomic.MpscChunkedAtomicArrayQueue;
28  import org.jctools.queues.atomic.MpscUnboundedAtomicArrayQueue;
29  import org.jctools.queues.atomic.SpscLinkedAtomicQueue;
30  import org.jctools.util.Pow2;
31  import org.jctools.util.UnsafeAccess;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.lang.invoke.MethodHandle;
36  import java.lang.invoke.MethodHandles;
37  import java.lang.reflect.Field;
38  import java.nio.ByteBuffer;
39  import java.nio.ByteOrder;
40  import java.nio.charset.StandardCharsets;
41  import java.nio.file.Files;
42  import java.nio.file.Path;
43  import java.nio.file.Paths;
44  import java.security.AccessController;
45  import java.security.PrivilegedAction;
46  import java.util.Arrays;
47  import java.util.Collections;
48  import java.util.Deque;
49  import java.util.HashSet;
50  import java.util.LinkedHashSet;
51  import java.util.List;
52  import java.util.Locale;
53  import java.util.Map;
54  import java.util.Queue;
55  import java.util.Random;
56  import java.util.Set;
57  import java.util.concurrent.ConcurrentHashMap;
58  import java.util.concurrent.ConcurrentLinkedDeque;
59  import java.util.concurrent.ConcurrentMap;
60  import java.util.concurrent.ThreadLocalRandom;
61  import java.util.concurrent.atomic.AtomicLong;
62  import java.util.regex.Matcher;
63  import java.util.regex.Pattern;
64  import java.util.stream.Stream;
65  
66  import static io.netty.util.internal.PlatformDependent0.HASH_CODE_ASCII_SEED;
67  import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C1;
68  import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C2;
69  import static io.netty.util.internal.PlatformDependent0.hashCodeAsciiSanitize;
70  import static io.netty.util.internal.PlatformDependent0.unalignedAccess;
71  import static java.lang.Math.max;
72  import static java.lang.Math.min;
73  import static java.lang.invoke.MethodType.methodType;
74  
75  /**
76   * Utility that detects various properties specific to the current runtime
77   * environment, such as Java version and the availability of the
78   * {@code sun.misc.Unsafe} object.
79   * <p>
80   * You can disable the use of {@code sun.misc.Unsafe} if you specify
81   * the system property <strong>io.netty.noUnsafe</strong>.
82   */
83  public final class PlatformDependent {
84  
85      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class);
86  
87      private static Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN;
88      private static final boolean MAYBE_SUPER_USER;
89  
90      private static final boolean CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !isAndroid();
91  
92      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause0();
93      private static final boolean DIRECT_BUFFER_PREFERRED;
94      private static final long MAX_DIRECT_MEMORY = estimateMaxDirectMemory();
95  
96      private static final int MPSC_CHUNK_SIZE =  1024;
97      private static final int MIN_MAX_MPSC_CAPACITY =  MPSC_CHUNK_SIZE * 2;
98      private static final int MAX_ALLOWED_MPSC_CAPACITY = Pow2.MAX_POW2;
99  
100     private static final long BYTE_ARRAY_BASE_OFFSET = byteArrayBaseOffset0();
101 
102     private static final File TMPDIR = tmpdir0();
103 
104     private static final int BIT_MODE = bitMode0();
105     private static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", ""));
106     private static final String NORMALIZED_OS = normalizeOs(SystemPropertyUtil.get("os.name", ""));
107 
108     // keep in sync with maven's pom.xml via os.detection.classifierWithLikes!
109     private static final String[] ALLOWED_LINUX_OS_CLASSIFIERS = {"fedora", "suse", "arch"};
110     private static final Set<String> LINUX_OS_CLASSIFIERS;
111 
112     private static final boolean IS_WINDOWS = isWindows0();
113     private static final boolean IS_OSX = isOsx0();
114     private static final boolean IS_J9_JVM = isJ9Jvm0();
115     private static final boolean IS_IVKVM_DOT_NET = isIkvmDotNet0();
116 
117     private static final int ADDRESS_SIZE = addressSize0();
118     private static final boolean USE_DIRECT_BUFFER_NO_CLEANER;
119     private static final AtomicLong DIRECT_MEMORY_COUNTER;
120     private static final long DIRECT_MEMORY_LIMIT;
121     private static final Cleaner CLEANER;
122     private static final boolean HAS_ALLOCATE_UNINIT_ARRAY;
123     // For specifications, see https://www.freedesktop.org/software/systemd/man/os-release.html
124     private static final String[] OS_RELEASE_FILES = {"/etc/os-release", "/usr/lib/os-release"};
125     private static final String LINUX_ID_PREFIX = "ID=";
126     private static final String LINUX_ID_LIKE_PREFIX = "ID_LIKE=";
127     public static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
128 
129     private static final Cleaner NOOP = new Cleaner() {
130         @Override
131         public void freeDirectBuffer(ByteBuffer buffer) {
132             // NOOP
133         }
134     };
135 
136     static {
137         // Here is how the system property is used:
138         //
139         // * <  0  - Don't use cleaner, and inherit max direct memory from java. In this case the
140         //           "practical max direct memory" would be 2 * max memory as defined by the JDK.
141         // * == 0  - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK.
142         // * >  0  - Don't use cleaner. This will limit Netty's total direct memory
143         //           (note: that JDK's direct memory limit is independent of this).
144         long maxDirectMemory = SystemPropertyUtil.getLong("io.netty.maxDirectMemory", -1);
145 
146         if (maxDirectMemory == 0 || !hasUnsafe() || !PlatformDependent0.hasDirectBufferNoCleanerConstructor()) {
147             USE_DIRECT_BUFFER_NO_CLEANER = false;
148             DIRECT_MEMORY_COUNTER = null;
149         } else {
150             USE_DIRECT_BUFFER_NO_CLEANER = true;
151             if (maxDirectMemory < 0) {
152                 maxDirectMemory = MAX_DIRECT_MEMORY;
153                 if (maxDirectMemory <= 0) {
154                     DIRECT_MEMORY_COUNTER = null;
155                 } else {
156                     DIRECT_MEMORY_COUNTER = new AtomicLong();
157                 }
158             } else {
159                 DIRECT_MEMORY_COUNTER = new AtomicLong();
160             }
161         }
162         logger.debug("-Dio.netty.maxDirectMemory: {} bytes", maxDirectMemory);
163         DIRECT_MEMORY_LIMIT = maxDirectMemory >= 1 ? maxDirectMemory : MAX_DIRECT_MEMORY;
164         HAS_ALLOCATE_UNINIT_ARRAY = javaVersion() >= 9 && PlatformDependent0.hasAllocateArrayMethod();
165 
166         MAYBE_SUPER_USER = maybeSuperUser0();
167 
168         if (!isAndroid()) {
169             // only direct to method if we are not running on android.
170             // See https://github.com/netty/netty/issues/2604
171             if (javaVersion() >= 9) {
172                 CLEANER = CleanerJava9.isSupported() ? new CleanerJava9() : NOOP;
173             } else {
174                 CLEANER = CleanerJava6.isSupported() ? new CleanerJava6() : NOOP;
175             }
176         } else {
177             CLEANER = NOOP;
178         }
179 
180         // We should always prefer direct buffers by default if we can use a Cleaner to release direct buffers.
181         DIRECT_BUFFER_PREFERRED = CLEANER != NOOP
182                                   && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
183         if (logger.isDebugEnabled()) {
184             logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED);
185         }
186 
187         /*
188          * We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe
189          * guard.
190          */
191         if (CLEANER == NOOP && !PlatformDependent0.isExplicitNoUnsafe()) {
192             logger.info(
193                     "Your platform does not provide complete low-level API for accessing direct buffers reliably. " +
194                     "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " +
195                     "instability.");
196         }
197 
198         final Set<String> allowedClassifiers = Collections.unmodifiableSet(
199                 new HashSet<String>(Arrays.asList(ALLOWED_LINUX_OS_CLASSIFIERS)));
200         final Set<String> availableClassifiers = new LinkedHashSet<String>();
201 
202         if (!addPropertyOsClassifiers(allowedClassifiers, availableClassifiers)) {
203             addFilesystemOsClassifiers(allowedClassifiers, availableClassifiers);
204         }
205         LINUX_OS_CLASSIFIERS = Collections.unmodifiableSet(availableClassifiers);
206     }
207 
208     static void addFilesystemOsClassifiers(final Set<String> allowedClassifiers,
209                                            final Set<String> availableClassifiers) {
210         for (final String osReleaseFileName : OS_RELEASE_FILES) {
211             final Path file = Paths.get(osReleaseFileName);
212             boolean found = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
213                 @Override
214                 public Boolean run() {
215                     Pattern lineSplitPattern = Pattern.compile("[ ]+");
216                     try {
217                         if (Files.exists(file)) {
218                             try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
219                                 lines.forEach(line -> {
220                                     if (line.startsWith(LINUX_ID_PREFIX)) {
221                                         String id = normalizeOsReleaseVariableValue(
222                                                 line.substring(LINUX_ID_PREFIX.length()));
223                                         addClassifier(allowedClassifiers, availableClassifiers, id);
224                                     } else if (line.startsWith(LINUX_ID_LIKE_PREFIX)) {
225                                         line = normalizeOsReleaseVariableValue(
226                                                 line.substring(LINUX_ID_LIKE_PREFIX.length()));
227                                         addClassifier(allowedClassifiers, availableClassifiers,
228                                                 lineSplitPattern.split(line));
229                                     }
230                                 });
231                             } catch (SecurityException e) {
232                                 logger.debug("Unable to read {}", osReleaseFileName, e);
233                             } catch (IOException e) {
234                                 logger.debug("Error while reading content of {}", osReleaseFileName, e);
235                             }
236                             // specification states we should only fall back if /etc/os-release does not exist
237                             return true;
238                         }
239                     } catch (SecurityException e) {
240                         logger.debug("Unable to check if {} exists", osReleaseFileName, e);
241                     }
242                     return false;
243                 }
244             });
245 
246             if (found) {
247                 break;
248             }
249         }
250     }
251 
252     static boolean addPropertyOsClassifiers(Set<String> allowedClassifiers, Set<String> availableClassifiers) {
253         // empty: -Dio.netty.osClassifiers (no distro specific classifiers for native libs)
254         // single ID: -Dio.netty.osClassifiers=ubuntu
255         // pair ID, ID_LIKE: -Dio.netty.osClassifiers=ubuntu,debian
256         // illegal otherwise
257         String osClassifiersPropertyName = "io.netty.osClassifiers";
258         String osClassifiers = SystemPropertyUtil.get(osClassifiersPropertyName);
259         if (osClassifiers == null) {
260             return false;
261         }
262         if (osClassifiers.isEmpty()) {
263             // let users omit classifiers with just -Dio.netty.osClassifiers
264             return true;
265         }
266         String[] classifiers = osClassifiers.split(",");
267         if (classifiers.length == 0) {
268             throw new IllegalArgumentException(
269                     osClassifiersPropertyName + " property is not empty, but contains no classifiers: "
270                             + osClassifiers);
271         }
272         // at most ID, ID_LIKE classifiers
273         if (classifiers.length > 2) {
274             throw new IllegalArgumentException(
275                     osClassifiersPropertyName + " property contains more than 2 classifiers: " + osClassifiers);
276         }
277         for (String classifier : classifiers) {
278             addClassifier(allowedClassifiers, availableClassifiers, classifier);
279         }
280         return true;
281     }
282 
283     public static long byteArrayBaseOffset() {
284         return BYTE_ARRAY_BASE_OFFSET;
285     }
286 
287     public static boolean hasDirectBufferNoCleanerConstructor() {
288         return PlatformDependent0.hasDirectBufferNoCleanerConstructor();
289     }
290 
291     public static byte[] allocateUninitializedArray(int size) {
292         return HAS_ALLOCATE_UNINIT_ARRAY ?  PlatformDependent0.allocateUninitializedArray(size) : new byte[size];
293     }
294 
295     /**
296      * Returns {@code true} if and only if the current platform is Android
297      */
298     public static boolean isAndroid() {
299         return PlatformDependent0.isAndroid();
300     }
301 
302     /**
303      * Return {@code true} if the JVM is running on Windows
304      */
305     public static boolean isWindows() {
306         return IS_WINDOWS;
307     }
308 
309     /**
310      * Return {@code true} if the JVM is running on OSX / MacOS
311      */
312     public static boolean isOsx() {
313         return IS_OSX;
314     }
315 
316     /**
317      * Return {@code true} if the current user may be a super-user. Be aware that this is just an hint and so it may
318      * return false-positives.
319      */
320     public static boolean maybeSuperUser() {
321         return MAYBE_SUPER_USER;
322     }
323 
324     /**
325      * Return the version of Java under which this library is used.
326      */
327     public static int javaVersion() {
328         return PlatformDependent0.javaVersion();
329     }
330 
331     /**
332      * Returns {@code true} if and only if it is fine to enable TCP_NODELAY socket option by default.
333      */
334     public static boolean canEnableTcpNoDelayByDefault() {
335         return CAN_ENABLE_TCP_NODELAY_BY_DEFAULT;
336     }
337 
338     /**
339      * Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used for accelerated
340      * direct memory access.
341      */
342     public static boolean hasUnsafe() {
343         return UNSAFE_UNAVAILABILITY_CAUSE == null;
344     }
345 
346     /**
347      * Return the reason (if any) why {@code sun.misc.Unsafe} was not available.
348      */
349     public static Throwable getUnsafeUnavailabilityCause() {
350         return UNSAFE_UNAVAILABILITY_CAUSE;
351     }
352 
353     /**
354      * {@code true} if and only if the platform supports unaligned access.
355      *
356      * @see <a href="https://en.wikipedia.org/wiki/Segmentation_fault#Bus_error">Wikipedia on segfault</a>
357      */
358     public static boolean isUnaligned() {
359         return PlatformDependent0.isUnaligned();
360     }
361 
362     /**
363      * Returns {@code true} if the platform has reliable low-level direct buffer access API and a user has not specified
364      * {@code -Dio.netty.noPreferDirect} option.
365      */
366     public static boolean directBufferPreferred() {
367         return DIRECT_BUFFER_PREFERRED;
368     }
369 
370     /**
371      * Returns the maximum memory reserved for direct buffer allocation.
372      */
373     public static long maxDirectMemory() {
374         return DIRECT_MEMORY_LIMIT;
375     }
376 
377     /**
378      * Returns the current memory reserved for direct buffer allocation.
379      * This method returns -1 in case that a value is not available.
380      *
381      * @see #maxDirectMemory()
382      */
383     public static long usedDirectMemory() {
384         return DIRECT_MEMORY_COUNTER != null ? DIRECT_MEMORY_COUNTER.get() : -1;
385     }
386 
387     /**
388      * Returns the temporary directory.
389      */
390     public static File tmpdir() {
391         return TMPDIR;
392     }
393 
394     /**
395      * Returns the bit mode of the current VM (usually 32 or 64.)
396      */
397     public static int bitMode() {
398         return BIT_MODE;
399     }
400 
401     /**
402      * Return the address size of the OS.
403      * 4 (for 32 bits systems ) and 8 (for 64 bits systems).
404      */
405     public static int addressSize() {
406         return ADDRESS_SIZE;
407     }
408 
409     public static long allocateMemory(long size) {
410         return PlatformDependent0.allocateMemory(size);
411     }
412 
413     public static void freeMemory(long address) {
414         PlatformDependent0.freeMemory(address);
415     }
416 
417     public static long reallocateMemory(long address, long newSize) {
418         return PlatformDependent0.reallocateMemory(address, newSize);
419     }
420 
421     /**
422      * Raises an exception bypassing compiler checks for checked exceptions.
423      */
424     public static void throwException(Throwable t) {
425         PlatformDependent0.throwException(t);
426     }
427 
428     /**
429      * Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
430      */
431     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap() {
432         return new ConcurrentHashMap<K, V>();
433     }
434 
435     /**
436      * Creates a new fastest {@link LongCounter} implementation for the current platform.
437      */
438     public static LongCounter newLongCounter() {
439         return new LongAdderCounter();
440     }
441 
442     /**
443      * Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
444      */
445     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity) {
446         return new ConcurrentHashMap<K, V>(initialCapacity);
447     }
448 
449     /**
450      * Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
451      */
452     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity, float loadFactor) {
453         return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor);
454     }
455 
456     /**
457      * Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
458      */
459     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(
460             int initialCapacity, float loadFactor, int concurrencyLevel) {
461         return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor, concurrencyLevel);
462     }
463 
464     /**
465      * Creates a new fastest {@link ConcurrentMap} implementation for the current platform.
466      */
467     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(Map<? extends K, ? extends V> map) {
468         return new ConcurrentHashMap<K, V>(map);
469     }
470 
471     /**
472      * Try to deallocate the specified direct {@link ByteBuffer}. Please note this method does nothing if
473      * the current platform does not support this operation or the specified buffer is not a direct buffer.
474      */
475     public static void freeDirectBuffer(ByteBuffer buffer) {
476         CLEANER.freeDirectBuffer(buffer);
477     }
478 
479     public static long directBufferAddress(ByteBuffer buffer) {
480         return PlatformDependent0.directBufferAddress(buffer);
481     }
482 
483     public static ByteBuffer directBuffer(long memoryAddress, int size) {
484         if (PlatformDependent0.hasDirectBufferNoCleanerConstructor()) {
485             return PlatformDependent0.newDirectBuffer(memoryAddress, size);
486         }
487         throw new UnsupportedOperationException(
488                 "sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not available");
489     }
490 
491     public static Object getObject(Object object, long fieldOffset) {
492         return PlatformDependent0.getObject(object, fieldOffset);
493     }
494 
495     public static int getInt(Object object, long fieldOffset) {
496         return PlatformDependent0.getInt(object, fieldOffset);
497     }
498 
499     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
500         PlatformDependent0.safeConstructPutInt(object, fieldOffset, value);
501     }
502 
503     public static int getIntVolatile(long address) {
504         return PlatformDependent0.getIntVolatile(address);
505     }
506 
507     public static void putIntOrdered(long adddress, int newValue) {
508         PlatformDependent0.putIntOrdered(adddress, newValue);
509     }
510 
511     public static byte getByte(long address) {
512         return PlatformDependent0.getByte(address);
513     }
514 
515     public static short getShort(long address) {
516         return PlatformDependent0.getShort(address);
517     }
518 
519     public static int getInt(long address) {
520         return PlatformDependent0.getInt(address);
521     }
522 
523     public static long getLong(long address) {
524         return PlatformDependent0.getLong(address);
525     }
526 
527     public static byte getByte(byte[] data, int index) {
528         return hasUnsafe() ? PlatformDependent0.getByte(data, index) : data[index];
529     }
530 
531     public static byte getByte(byte[] data, long index) {
532         return hasUnsafe() ? PlatformDependent0.getByte(data, index) : data[toIntExact(index)];
533     }
534 
535     public static short getShort(byte[] data, int index) {
536         return hasUnsafe() ? PlatformDependent0.getShort(data, index) : data[index];
537     }
538 
539     public static int getInt(byte[] data, int index) {
540         return hasUnsafe() ? PlatformDependent0.getInt(data, index) : data[index];
541     }
542 
543     public static int getInt(int[] data, long index) {
544         return hasUnsafe() ? PlatformDependent0.getInt(data, index) : data[toIntExact(index)];
545     }
546 
547     public static long getLong(byte[] data, int index) {
548         return hasUnsafe() ? PlatformDependent0.getLong(data, index) : data[index];
549     }
550 
551     public static long getLong(long[] data, long index) {
552         return hasUnsafe() ? PlatformDependent0.getLong(data, index) : data[toIntExact(index)];
553     }
554 
555     private static int toIntExact(long value) {
556         if (javaVersion() >= 8) {
557             return toIntExact8(value);
558         }
559         int result = (int) value;
560         if (result != value) {
561             throw new ArithmeticException("Cannot convert to exact int: " + value);
562         }
563         return result;
564     }
565 
566     @SuppressJava6Requirement(reason = "version checked")
567     private static int toIntExact8(long value) {
568         return Math.toIntExact(value);
569     }
570 
571     private static long getLongSafe(byte[] bytes, int offset) {
572         if (BIG_ENDIAN_NATIVE_ORDER) {
573             return (long) bytes[offset] << 56 |
574                     ((long) bytes[offset + 1] & 0xff) << 48 |
575                     ((long) bytes[offset + 2] & 0xff) << 40 |
576                     ((long) bytes[offset + 3] & 0xff) << 32 |
577                     ((long) bytes[offset + 4] & 0xff) << 24 |
578                     ((long) bytes[offset + 5] & 0xff) << 16 |
579                     ((long) bytes[offset + 6] & 0xff) <<  8 |
580                     (long) bytes[offset + 7] & 0xff;
581         }
582         return (long) bytes[offset] & 0xff |
583                 ((long) bytes[offset + 1] & 0xff) << 8 |
584                 ((long) bytes[offset + 2] & 0xff) << 16 |
585                 ((long) bytes[offset + 3] & 0xff) << 24 |
586                 ((long) bytes[offset + 4] & 0xff) << 32 |
587                 ((long) bytes[offset + 5] & 0xff) << 40 |
588                 ((long) bytes[offset + 6] & 0xff) << 48 |
589                 (long) bytes[offset + 7] << 56;
590     }
591 
592     private static int getIntSafe(byte[] bytes, int offset) {
593         if (BIG_ENDIAN_NATIVE_ORDER) {
594             return bytes[offset] << 24 |
595                     (bytes[offset + 1] & 0xff) << 16 |
596                     (bytes[offset + 2] & 0xff) << 8 |
597                     bytes[offset + 3] & 0xff;
598         }
599         return bytes[offset] & 0xff |
600                 (bytes[offset + 1] & 0xff) << 8 |
601                 (bytes[offset + 2] & 0xff) << 16 |
602                 bytes[offset + 3] << 24;
603     }
604 
605     private static short getShortSafe(byte[] bytes, int offset) {
606         if (BIG_ENDIAN_NATIVE_ORDER) {
607             return (short) (bytes[offset] << 8 | (bytes[offset + 1] & 0xff));
608         }
609         return (short) (bytes[offset] & 0xff | (bytes[offset + 1] << 8));
610     }
611 
612     /**
613      * Identical to {@link PlatformDependent0#hashCodeAsciiCompute(long, int)} but for {@link CharSequence}.
614      */
615     private static int hashCodeAsciiCompute(CharSequence value, int offset, int hash) {
616         if (BIG_ENDIAN_NATIVE_ORDER) {
617             return hash * HASH_CODE_C1 +
618                     // Low order int
619                     hashCodeAsciiSanitizeInt(value, offset + 4) * HASH_CODE_C2 +
620                     // High order int
621                     hashCodeAsciiSanitizeInt(value, offset);
622         }
623         return hash * HASH_CODE_C1 +
624                 // Low order int
625                 hashCodeAsciiSanitizeInt(value, offset) * HASH_CODE_C2 +
626                 // High order int
627                 hashCodeAsciiSanitizeInt(value, offset + 4);
628     }
629 
630     /**
631      * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(int)} but for {@link CharSequence}.
632      */
633     private static int hashCodeAsciiSanitizeInt(CharSequence value, int offset) {
634         if (BIG_ENDIAN_NATIVE_ORDER) {
635             // mimic a unsafe.getInt call on a big endian machine
636             return (value.charAt(offset + 3) & 0x1f) |
637                    (value.charAt(offset + 2) & 0x1f) << 8 |
638                    (value.charAt(offset + 1) & 0x1f) << 16 |
639                    (value.charAt(offset) & 0x1f) << 24;
640         }
641         return (value.charAt(offset + 3) & 0x1f) << 24 |
642                (value.charAt(offset + 2) & 0x1f) << 16 |
643                (value.charAt(offset + 1) & 0x1f) << 8 |
644                (value.charAt(offset) & 0x1f);
645     }
646 
647     /**
648      * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(short)} but for {@link CharSequence}.
649      */
650     private static int hashCodeAsciiSanitizeShort(CharSequence value, int offset) {
651         if (BIG_ENDIAN_NATIVE_ORDER) {
652             // mimic a unsafe.getShort call on a big endian machine
653             return (value.charAt(offset + 1) & 0x1f) |
654                     (value.charAt(offset) & 0x1f) << 8;
655         }
656         return (value.charAt(offset + 1) & 0x1f) << 8 |
657                 (value.charAt(offset) & 0x1f);
658     }
659 
660     /**
661      * Identical to {@link PlatformDependent0#hashCodeAsciiSanitize(byte)} but for {@link CharSequence}.
662      */
663     private static int hashCodeAsciiSanitizeByte(char value) {
664         return value & 0x1f;
665     }
666 
667     public static void putByte(long address, byte value) {
668         PlatformDependent0.putByte(address, value);
669     }
670 
671     public static void putShort(long address, short value) {
672         PlatformDependent0.putShort(address, value);
673     }
674 
675     public static void putInt(long address, int value) {
676         PlatformDependent0.putInt(address, value);
677     }
678 
679     public static void putLong(long address, long value) {
680         PlatformDependent0.putLong(address, value);
681     }
682 
683     public static void putByte(byte[] data, int index, byte value) {
684         PlatformDependent0.putByte(data, index, value);
685     }
686 
687     public static void putByte(Object data, long offset, byte value) {
688         PlatformDependent0.putByte(data, offset, value);
689     }
690 
691     public static void putShort(byte[] data, int index, short value) {
692         PlatformDependent0.putShort(data, index, value);
693     }
694 
695     public static void putInt(byte[] data, int index, int value) {
696         PlatformDependent0.putInt(data, index, value);
697     }
698 
699     public static void putLong(byte[] data, int index, long value) {
700         PlatformDependent0.putLong(data, index, value);
701     }
702 
703     public static void putObject(Object o, long offset, Object x) {
704         PlatformDependent0.putObject(o, offset, x);
705     }
706 
707     public static long objectFieldOffset(Field field) {
708         return PlatformDependent0.objectFieldOffset(field);
709     }
710 
711     public static void copyMemory(long srcAddr, long dstAddr, long length) {
712         PlatformDependent0.copyMemory(srcAddr, dstAddr, length);
713     }
714 
715     public static void copyMemory(byte[] src, int srcIndex, long dstAddr, long length) {
716         PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length);
717     }
718 
719     public static void copyMemory(byte[] src, int srcIndex, byte[] dst, int dstIndex, long length) {
720         PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex,
721                                       dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
722     }
723 
724     public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) {
725         PlatformDependent0.copyMemory(null, srcAddr, dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
726     }
727 
728     public static void setMemory(byte[] dst, int dstIndex, long bytes, byte value) {
729         PlatformDependent0.setMemory(dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, bytes, value);
730     }
731 
732     public static void setMemory(long address, long bytes, byte value) {
733         PlatformDependent0.setMemory(address, bytes, value);
734     }
735 
736     /**
737      * Allocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s allocated with
738      * this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}.
739      */
740     public static ByteBuffer allocateDirectNoCleaner(int capacity) {
741         assert USE_DIRECT_BUFFER_NO_CLEANER;
742 
743         incrementMemoryCounter(capacity);
744         try {
745             return PlatformDependent0.allocateDirectNoCleaner(capacity);
746         } catch (Throwable e) {
747             decrementMemoryCounter(capacity);
748             throwException(e);
749             return null;
750         }
751     }
752 
753     /**
754      * Reallocate a new {@link ByteBuffer} with the given {@code capacity}. {@link ByteBuffer}s reallocated with
755      * this method <strong>MUST</strong> be deallocated via {@link #freeDirectNoCleaner(ByteBuffer)}.
756      */
757     public static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
758         assert USE_DIRECT_BUFFER_NO_CLEANER;
759 
760         int len = capacity - buffer.capacity();
761         incrementMemoryCounter(len);
762         try {
763             return PlatformDependent0.reallocateDirectNoCleaner(buffer, capacity);
764         } catch (Throwable e) {
765             decrementMemoryCounter(len);
766             throwException(e);
767             return null;
768         }
769     }
770 
771     /**
772      * This method <strong>MUST</strong> only be called for {@link ByteBuffer}s that were allocated via
773      * {@link #allocateDirectNoCleaner(int)}.
774      */
775     public static void freeDirectNoCleaner(ByteBuffer buffer) {
776         assert USE_DIRECT_BUFFER_NO_CLEANER;
777 
778         int capacity = buffer.capacity();
779         PlatformDependent0.freeMemory(PlatformDependent0.directBufferAddress(buffer));
780         decrementMemoryCounter(capacity);
781     }
782 
783     public static boolean hasAlignDirectByteBuffer() {
784         return hasUnsafe() || PlatformDependent0.hasAlignSliceMethod();
785     }
786 
787     public static ByteBuffer alignDirectBuffer(ByteBuffer buffer, int alignment) {
788         if (!buffer.isDirect()) {
789             throw new IllegalArgumentException("Cannot get aligned slice of non-direct byte buffer.");
790         }
791         if (PlatformDependent0.hasAlignSliceMethod()) {
792             return PlatformDependent0.alignSlice(buffer, alignment);
793         }
794         if (hasUnsafe()) {
795             long address = directBufferAddress(buffer);
796             long aligned = align(address, alignment);
797             buffer.position((int) (aligned - address));
798             return buffer.slice();
799         }
800         // We don't have enough information to be able to align any buffers.
801         throw new UnsupportedOperationException("Cannot align direct buffer. " +
802                 "Needs either Unsafe or ByteBuffer.alignSlice method available.");
803     }
804 
805     public static long align(long value, int alignment) {
806         return Pow2.align(value, alignment);
807     }
808 
809     private static void incrementMemoryCounter(int capacity) {
810         if (DIRECT_MEMORY_COUNTER != null) {
811             long newUsedMemory = DIRECT_MEMORY_COUNTER.addAndGet(capacity);
812             if (newUsedMemory > DIRECT_MEMORY_LIMIT) {
813                 DIRECT_MEMORY_COUNTER.addAndGet(-capacity);
814                 throw new OutOfDirectMemoryError("failed to allocate " + capacity
815                         + " byte(s) of direct memory (used: " + (newUsedMemory - capacity)
816                         + ", max: " + DIRECT_MEMORY_LIMIT + ')');
817             }
818         }
819     }
820 
821     private static void decrementMemoryCounter(int capacity) {
822         if (DIRECT_MEMORY_COUNTER != null) {
823             long usedMemory = DIRECT_MEMORY_COUNTER.addAndGet(-capacity);
824             assert usedMemory >= 0;
825         }
826     }
827 
828     public static boolean useDirectBufferNoCleaner() {
829         return USE_DIRECT_BUFFER_NO_CLEANER;
830     }
831 
832     /**
833      * Compare two {@code byte} arrays for equality. For performance reasons no bounds checking on the
834      * parameters is performed.
835      *
836      * @param bytes1 the first byte array.
837      * @param startPos1 the position (inclusive) to start comparing in {@code bytes1}.
838      * @param bytes2 the second byte array.
839      * @param startPos2 the position (inclusive) to start comparing in {@code bytes2}.
840      * @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds
841      * by the caller.
842      */
843     public static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
844         if (javaVersion() > 8 && (startPos2 | startPos1 | (bytes1.length - length) | bytes2.length - length) == 0) {
845             return Arrays.equals(bytes1, bytes2);
846         }
847         return !hasUnsafe() || !unalignedAccess() ?
848                   equalsSafe(bytes1, startPos1, bytes2, startPos2, length) :
849                   PlatformDependent0.equals(bytes1, startPos1, bytes2, startPos2, length);
850     }
851 
852     /**
853      * Determine if a subsection of an array is zero.
854      * @param bytes The byte array.
855      * @param startPos The starting index (inclusive) in {@code bytes}.
856      * @param length The amount of bytes to check for zero.
857      * @return {@code false} if {@code bytes[startPos:startsPos+length)} contains a value other than zero.
858      */
859     public static boolean isZero(byte[] bytes, int startPos, int length) {
860         return !hasUnsafe() || !unalignedAccess() ?
861                 isZeroSafe(bytes, startPos, length) :
862                 PlatformDependent0.isZero(bytes, startPos, length);
863     }
864 
865     /**
866      * Compare two {@code byte} arrays for equality without leaking timing information.
867      * For performance reasons no bounds checking on the parameters is performed.
868      * <p>
869      * The {@code int} return type is intentional and is designed to allow cascading of constant time operations:
870      * <pre>
871      *     byte[] s1 = new {1, 2, 3};
872      *     byte[] s2 = new {1, 2, 3};
873      *     byte[] s3 = new {1, 2, 3};
874      *     byte[] s4 = new {4, 5, 6};
875      *     boolean equals = (equalsConstantTime(s1, 0, s2, 0, s1.length) &
876      *                       equalsConstantTime(s3, 0, s4, 0, s3.length)) != 0;
877      * </pre>
878      * @param bytes1 the first byte array.
879      * @param startPos1 the position (inclusive) to start comparing in {@code bytes1}.
880      * @param bytes2 the second byte array.
881      * @param startPos2 the position (inclusive) to start comparing in {@code bytes2}.
882      * @param length the amount of bytes to compare. This is assumed to be validated as not going out of bounds
883      * by the caller.
884      * @return {@code 0} if not equal. {@code 1} if equal.
885      */
886     public static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
887         return !hasUnsafe() || !unalignedAccess() ?
888                   ConstantTimeUtils.equalsConstantTime(bytes1, startPos1, bytes2, startPos2, length) :
889                   PlatformDependent0.equalsConstantTime(bytes1, startPos1, bytes2, startPos2, length);
890     }
891 
892     /**
893      * Calculate a hash code of a byte array assuming ASCII character encoding.
894      * The resulting hash code will be case insensitive.
895      * @param bytes The array which contains the data to hash.
896      * @param startPos What index to start generating a hash code in {@code bytes}
897      * @param length The amount of bytes that should be accounted for in the computation.
898      * @return The hash code of {@code bytes} assuming ASCII character encoding.
899      * The resulting hash code will be case insensitive.
900      */
901     public static int hashCodeAscii(byte[] bytes, int startPos, int length) {
902         return !hasUnsafe() || !unalignedAccess() ?
903                 hashCodeAsciiSafe(bytes, startPos, length) :
904                 PlatformDependent0.hashCodeAscii(bytes, startPos, length);
905     }
906 
907     /**
908      * Calculate a hash code of a byte array assuming ASCII character encoding.
909      * The resulting hash code will be case insensitive.
910      * <p>
911      * This method assumes that {@code bytes} is equivalent to a {@code byte[]} but just using {@link CharSequence}
912      * for storage. The upper most byte of each {@code char} from {@code bytes} is ignored.
913      * @param bytes The array which contains the data to hash (assumed to be equivalent to a {@code byte[]}).
914      * @return The hash code of {@code bytes} assuming ASCII character encoding.
915      * The resulting hash code will be case insensitive.
916      */
917     public static int hashCodeAscii(CharSequence bytes) {
918         final int length = bytes.length();
919         final int remainingBytes = length & 7;
920         int hash = HASH_CODE_ASCII_SEED;
921         // Benchmarking shows that by just naively looping for inputs 8~31 bytes long we incur a relatively large
922         // performance penalty (only achieve about 60% performance of loop which iterates over each char). So because
923         // of this we take special provisions to unroll the looping for these conditions.
924         if (length >= 32) {
925             for (int i = length - 8; i >= remainingBytes; i -= 8) {
926                 hash = hashCodeAsciiCompute(bytes, i, hash);
927             }
928         } else if (length >= 8) {
929             hash = hashCodeAsciiCompute(bytes, length - 8, hash);
930             if (length >= 16) {
931                 hash = hashCodeAsciiCompute(bytes, length - 16, hash);
932                 if (length >= 24) {
933                     hash = hashCodeAsciiCompute(bytes, length - 24, hash);
934                 }
935             }
936         }
937         if (remainingBytes == 0) {
938             return hash;
939         }
940         int offset = 0;
941         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
942             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0));
943             offset = 1;
944         }
945         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
946             hash = hash * (offset == 0 ? HASH_CODE_C1 : HASH_CODE_C2)
947                     + hashCodeAsciiSanitize(hashCodeAsciiSanitizeShort(bytes, offset));
948             offset += 2;
949         }
950         if (remainingBytes >= 4) { // 4, 5, 6, 7
951             return hash * ((offset == 0 | offset == 3) ? HASH_CODE_C1 : HASH_CODE_C2)
952                     + hashCodeAsciiSanitizeInt(bytes, offset);
953         }
954         return hash;
955     }
956 
957     private static final class Mpsc {
958         private static final boolean USE_MPSC_CHUNKED_ARRAY_QUEUE;
959 
960         private Mpsc() {
961         }
962 
963         static {
964             Object unsafe = null;
965             if (hasUnsafe()) {
966                 // jctools goes through its own process of initializing unsafe; of
967                 // course, this requires permissions which might not be granted to calling code, so we
968                 // must mark this block as privileged too
969                 unsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
970                     @Override
971                     public Object run() {
972                         // force JCTools to initialize unsafe. We do this via reflection so we don't reference
973                         // sun.misc.Unsafe.
974                         try {
975                             return UnsafeAccess.class.getField("UNSAFE");
976                         } catch (NoSuchFieldException | SecurityException e) {
977                             return e;
978                         }
979                     }
980                 });
981             }
982 
983             if (unsafe == null || unsafe instanceof Throwable) {
984                 logger.debug("org.jctools-core.MpscChunkedArrayQueue: unavailable");
985                 USE_MPSC_CHUNKED_ARRAY_QUEUE = false;
986             } else {
987                 logger.debug("org.jctools-core.MpscChunkedArrayQueue: available");
988                 USE_MPSC_CHUNKED_ARRAY_QUEUE = true;
989             }
990         }
991 
992         static <T> Queue<T> newMpscQueue(final int maxCapacity) {
993             // Calculate the max capacity which can not be bigger than MAX_ALLOWED_MPSC_CAPACITY.
994             // This is forced by the MpscChunkedArrayQueue implementation as will try to round it
995             // up to the next power of two and so will overflow otherwise.
996             final int capacity = max(min(maxCapacity, MAX_ALLOWED_MPSC_CAPACITY), MIN_MAX_MPSC_CAPACITY);
997             return newChunkedMpscQueue(MPSC_CHUNK_SIZE, capacity);
998         }
999 
1000         static <T> Queue<T> newChunkedMpscQueue(final int chunkSize, final int capacity) {
1001             return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscChunkedArrayQueue<T>(chunkSize, capacity)
1002                     : new MpscChunkedAtomicArrayQueue<T>(chunkSize, capacity);
1003         }
1004 
1005         static <T> Queue<T> newMpscQueue() {
1006             return USE_MPSC_CHUNKED_ARRAY_QUEUE ? new MpscUnboundedArrayQueue<T>(MPSC_CHUNK_SIZE)
1007                                                 : new MpscUnboundedAtomicArrayQueue<T>(MPSC_CHUNK_SIZE);
1008         }
1009     }
1010 
1011     /**
1012      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
1013      * consumer (one thread!).
1014      * @return A MPSC queue which may be unbounded.
1015      */
1016     public static <T> Queue<T> newMpscQueue() {
1017         return Mpsc.newMpscQueue();
1018     }
1019 
1020     /**
1021      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
1022      * consumer (one thread!).
1023      */
1024     public static <T> Queue<T> newMpscQueue(final int maxCapacity) {
1025         return Mpsc.newMpscQueue(maxCapacity);
1026     }
1027 
1028     /**
1029      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
1030      * consumer (one thread!).
1031      * The queue will grow and shrink its capacity in units of the given chunk size.
1032      */
1033     public static <T> Queue<T> newMpscQueue(final int chunkSize, final int maxCapacity) {
1034         return Mpsc.newChunkedMpscQueue(chunkSize, maxCapacity);
1035     }
1036 
1037     /**
1038      * Create a new {@link Queue} which is safe to use for single producer (one thread!) and a single
1039      * consumer (one thread!).
1040      */
1041     public static <T> Queue<T> newSpscQueue() {
1042         return hasUnsafe() ? new SpscLinkedQueue<T>() : new SpscLinkedAtomicQueue<T>();
1043     }
1044 
1045     /**
1046      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
1047      * consumer (one thread!) with the given fixes {@code capacity}.
1048      */
1049     public static <T> Queue<T> newFixedMpscQueue(int capacity) {
1050         return hasUnsafe() ? new MpscArrayQueue<T>(capacity) : new MpscAtomicArrayQueue<T>(capacity);
1051     }
1052 
1053     /**
1054      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and multiple
1055      * consumers with the given fixes {@code capacity}.
1056      */
1057     public static <T> Queue<T> newFixedMpmcQueue(int capacity) {
1058         return hasUnsafe() ? new MpmcArrayQueue<T>(capacity) : new MpmcAtomicArrayQueue<T>(capacity);
1059     }
1060 
1061     /**
1062      * Return the {@link ClassLoader} for the given {@link Class}.
1063      */
1064     public static ClassLoader getClassLoader(final Class<?> clazz) {
1065         return PlatformDependent0.getClassLoader(clazz);
1066     }
1067 
1068     /**
1069      * Return the context {@link ClassLoader} for the current {@link Thread}.
1070      */
1071     public static ClassLoader getContextClassLoader() {
1072         return PlatformDependent0.getContextClassLoader();
1073     }
1074 
1075     /**
1076      * Return the system {@link ClassLoader}.
1077      */
1078     public static ClassLoader getSystemClassLoader() {
1079         return PlatformDependent0.getSystemClassLoader();
1080     }
1081 
1082     /**
1083      * Returns a new concurrent {@link Deque}.
1084      */
1085     public static <C> Deque<C> newConcurrentDeque() {
1086         return new ConcurrentLinkedDeque<C>();
1087     }
1088 
1089     /**
1090      * Return a {@link Random} which is not-threadsafe and so can only be used from the same thread.
1091      */
1092     public static Random threadLocalRandom() {
1093         return ThreadLocalRandom.current();
1094     }
1095 
1096     private static boolean isWindows0() {
1097         boolean windows = "windows".equals(NORMALIZED_OS);
1098         if (windows) {
1099             logger.debug("Platform: Windows");
1100         }
1101         return windows;
1102     }
1103 
1104     private static boolean isOsx0() {
1105         boolean osx = "osx".equals(NORMALIZED_OS);
1106         if (osx) {
1107             logger.debug("Platform: MacOS");
1108         }
1109         return osx;
1110     }
1111 
1112     private static boolean maybeSuperUser0() {
1113         String username = SystemPropertyUtil.get("user.name");
1114         if (isWindows()) {
1115             return "Administrator".equals(username);
1116         }
1117         // Check for root and toor as some BSDs have a toor user that is basically the same as root.
1118         return "root".equals(username) || "toor".equals(username);
1119     }
1120 
1121     private static Throwable unsafeUnavailabilityCause0() {
1122         if (isAndroid()) {
1123             logger.debug("sun.misc.Unsafe: unavailable (Android)");
1124             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (Android)");
1125         }
1126 
1127         if (isIkvmDotNet()) {
1128             logger.debug("sun.misc.Unsafe: unavailable (IKVM.NET)");
1129             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (IKVM.NET)");
1130         }
1131 
1132         Throwable cause = PlatformDependent0.getUnsafeUnavailabilityCause();
1133         if (cause != null) {
1134             return cause;
1135         }
1136 
1137         try {
1138             boolean hasUnsafe = PlatformDependent0.hasUnsafe();
1139             logger.debug("sun.misc.Unsafe: {}", hasUnsafe ? "available" : "unavailable");
1140             return hasUnsafe ? null : PlatformDependent0.getUnsafeUnavailabilityCause();
1141         } catch (Throwable t) {
1142             logger.trace("Could not determine if Unsafe is available", t);
1143             // Probably failed to initialize PlatformDependent0.
1144             return new UnsupportedOperationException("Could not determine if Unsafe is available", t);
1145         }
1146     }
1147 
1148     /**
1149      * Returns {@code true} if the running JVM is either <a href="https://developer.ibm.com/javasdk/">IBM J9</a> or
1150      * <a href="https://www.eclipse.org/openj9/">Eclipse OpenJ9</a>, {@code false} otherwise.
1151      */
1152     public static boolean isJ9Jvm() {
1153         return IS_J9_JVM;
1154     }
1155 
1156     private static boolean isJ9Jvm0() {
1157         String vmName = SystemPropertyUtil.get("java.vm.name", "").toLowerCase();
1158         return vmName.startsWith("ibm j9") || vmName.startsWith("eclipse openj9");
1159     }
1160 
1161     /**
1162      * Returns {@code true} if the running JVM is <a href="https://www.ikvm.net">IKVM.NET</a>, {@code false} otherwise.
1163      */
1164     public static boolean isIkvmDotNet() {
1165         return IS_IVKVM_DOT_NET;
1166     }
1167 
1168     private static boolean isIkvmDotNet0() {
1169         String vmName = SystemPropertyUtil.get("java.vm.name", "").toUpperCase(Locale.US);
1170         return vmName.equals("IKVM.NET");
1171     }
1172 
1173     private static Pattern getMaxDirectMemorySizeArgPattern() {
1174         // Pattern's is immutable so it's always safe published
1175         Pattern pattern = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN;
1176         if (pattern == null) {
1177             pattern = Pattern.compile("\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$");
1178             MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN =  pattern;
1179         }
1180         return pattern;
1181     }
1182 
1183     /**
1184      * Compute an estimate of the maximum amount of direct memory available to this JVM.
1185      * <p>
1186      * The computation is not cached, so you probably want to use {@link #maxDirectMemory()} instead.
1187      * <p>
1188      * This will produce debug log output when called.
1189      *
1190      * @return The estimated max direct memory, in bytes.
1191      */
1192     @SuppressWarnings("unchecked")
1193     public static long estimateMaxDirectMemory() {
1194         long maxDirectMemory = PlatformDependent0.bitsMaxDirectMemory();
1195         if (maxDirectMemory > 0) {
1196             return maxDirectMemory;
1197         }
1198 
1199         try {
1200             // Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it.
1201             // Note that we are using reflection because Android doesn't have these classes.
1202             ClassLoader systemClassLoader = getSystemClassLoader();
1203             Class<?> mgmtFactoryClass = Class.forName(
1204                     "java.lang.management.ManagementFactory", true, systemClassLoader);
1205             Class<?> runtimeClass = Class.forName(
1206                     "java.lang.management.RuntimeMXBean", true, systemClassLoader);
1207 
1208             MethodHandles.Lookup lookup = MethodHandles.publicLookup();
1209             MethodHandle getRuntime = lookup.findStatic(
1210                     mgmtFactoryClass, "getRuntimeMXBean", methodType(runtimeClass));
1211             MethodHandle getInputArguments = lookup.findVirtual(
1212                     runtimeClass, "getInputArguments", methodType(List.class));
1213             List<String> vmArgs = (List<String>) getInputArguments.invoke(getRuntime.invoke());
1214 
1215             Pattern maxDirectMemorySizeArgPattern = getMaxDirectMemorySizeArgPattern();
1216 
1217             for (int i = vmArgs.size() - 1; i >= 0; i --) {
1218                 Matcher m = maxDirectMemorySizeArgPattern.matcher(vmArgs.get(i));
1219                 if (!m.matches()) {
1220                     continue;
1221                 }
1222 
1223                 maxDirectMemory = Long.parseLong(m.group(1));
1224                 switch (m.group(2).charAt(0)) {
1225                     case 'k': case 'K':
1226                         maxDirectMemory *= 1024;
1227                         break;
1228                     case 'm': case 'M':
1229                         maxDirectMemory *= 1024 * 1024;
1230                         break;
1231                     case 'g': case 'G':
1232                         maxDirectMemory *= 1024 * 1024 * 1024;
1233                         break;
1234                     default:
1235                         break;
1236                 }
1237                 break;
1238             }
1239         } catch (Throwable ignored) {
1240             // Ignore
1241         }
1242 
1243         if (maxDirectMemory <= 0) {
1244             maxDirectMemory = Runtime.getRuntime().maxMemory();
1245             logger.debug("maxDirectMemory: {} bytes (maybe)", maxDirectMemory);
1246         } else {
1247             logger.debug("maxDirectMemory: {} bytes", maxDirectMemory);
1248         }
1249 
1250         return maxDirectMemory;
1251     }
1252 
1253     private static File tmpdir0() {
1254         File f;
1255         try {
1256             f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir"));
1257             if (f != null) {
1258                 logger.debug("-Dio.netty.tmpdir: {}", f);
1259                 return f;
1260             }
1261 
1262             f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir"));
1263             if (f != null) {
1264                 logger.debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)", f);
1265                 return f;
1266             }
1267 
1268             // This shouldn't happen, but just in case ..
1269             if (isWindows()) {
1270                 f = toDirectory(System.getenv("TEMP"));
1271                 if (f != null) {
1272                     logger.debug("-Dio.netty.tmpdir: {} (%TEMP%)", f);
1273                     return f;
1274                 }
1275 
1276                 String userprofile = System.getenv("USERPROFILE");
1277                 if (userprofile != null) {
1278                     f = toDirectory(userprofile + "\\AppData\\Local\\Temp");
1279                     if (f != null) {
1280                         logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f);
1281                         return f;
1282                     }
1283 
1284                     f = toDirectory(userprofile + "\\Local Settings\\Temp");
1285                     if (f != null) {
1286                         logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f);
1287                         return f;
1288                     }
1289                 }
1290             } else {
1291                 f = toDirectory(System.getenv("TMPDIR"));
1292                 if (f != null) {
1293                     logger.debug("-Dio.netty.tmpdir: {} ($TMPDIR)", f);
1294                     return f;
1295                 }
1296             }
1297         } catch (Throwable ignored) {
1298             // Environment variable inaccessible
1299         }
1300 
1301         // Last resort.
1302         if (isWindows()) {
1303             f = new File("C:\\Windows\\Temp");
1304         } else {
1305             f = new File("/tmp");
1306         }
1307 
1308         logger.warn("Failed to get the temporary directory; falling back to: {}", f);
1309         return f;
1310     }
1311 
1312     @SuppressWarnings("ResultOfMethodCallIgnored")
1313     private static File toDirectory(String path) {
1314         if (path == null) {
1315             return null;
1316         }
1317 
1318         File f = new File(path);
1319         f.mkdirs();
1320 
1321         if (!f.isDirectory()) {
1322             return null;
1323         }
1324 
1325         try {
1326             return f.getAbsoluteFile();
1327         } catch (Exception ignored) {
1328             return f;
1329         }
1330     }
1331 
1332     private static int bitMode0() {
1333         // Check user-specified bit mode first.
1334         int bitMode = SystemPropertyUtil.getInt("io.netty.bitMode", 0);
1335         if (bitMode > 0) {
1336             logger.debug("-Dio.netty.bitMode: {}", bitMode);
1337             return bitMode;
1338         }
1339 
1340         // And then the vendor specific ones which is probably most reliable.
1341         bitMode = SystemPropertyUtil.getInt("sun.arch.data.model", 0);
1342         if (bitMode > 0) {
1343             logger.debug("-Dio.netty.bitMode: {} (sun.arch.data.model)", bitMode);
1344             return bitMode;
1345         }
1346         bitMode = SystemPropertyUtil.getInt("com.ibm.vm.bitmode", 0);
1347         if (bitMode > 0) {
1348             logger.debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)", bitMode);
1349             return bitMode;
1350         }
1351 
1352         // os.arch also gives us a good hint.
1353         String arch = SystemPropertyUtil.get("os.arch", "").toLowerCase(Locale.US).trim();
1354         if ("amd64".equals(arch) || "x86_64".equals(arch)) {
1355             bitMode = 64;
1356         } else if ("i386".equals(arch) || "i486".equals(arch) || "i586".equals(arch) || "i686".equals(arch)) {
1357             bitMode = 32;
1358         }
1359 
1360         if (bitMode > 0) {
1361             logger.debug("-Dio.netty.bitMode: {} (os.arch: {})", bitMode, arch);
1362         }
1363 
1364         // Last resort: guess from VM name and then fall back to most common 64-bit mode.
1365         String vm = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(Locale.US);
1366         Pattern bitPattern = Pattern.compile("([1-9][0-9]+)-?bit");
1367         Matcher m = bitPattern.matcher(vm);
1368         if (m.find()) {
1369             return Integer.parseInt(m.group(1));
1370         } else {
1371             return 64;
1372         }
1373     }
1374 
1375     private static int addressSize0() {
1376         if (!hasUnsafe()) {
1377             return -1;
1378         }
1379         return PlatformDependent0.addressSize();
1380     }
1381 
1382     private static long byteArrayBaseOffset0() {
1383         if (!hasUnsafe()) {
1384             return -1;
1385         }
1386         return PlatformDependent0.byteArrayBaseOffset();
1387     }
1388 
1389     private static boolean equalsSafe(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
1390         final int end = startPos1 + length;
1391         for (; startPos1 < end; ++startPos1, ++startPos2) {
1392             if (bytes1[startPos1] != bytes2[startPos2]) {
1393                 return false;
1394             }
1395         }
1396         return true;
1397     }
1398 
1399     private static boolean isZeroSafe(byte[] bytes, int startPos, int length) {
1400         final int end = startPos + length;
1401         for (; startPos < end; ++startPos) {
1402             if (bytes[startPos] != 0) {
1403                 return false;
1404             }
1405         }
1406         return true;
1407     }
1408 
1409     /**
1410      * Package private for testing purposes only!
1411      */
1412     static int hashCodeAsciiSafe(byte[] bytes, int startPos, int length) {
1413         int hash = HASH_CODE_ASCII_SEED;
1414         final int remainingBytes = length & 7;
1415         final int end = startPos + remainingBytes;
1416         for (int i = startPos - 8 + length; i >= end; i -= 8) {
1417             hash = PlatformDependent0.hashCodeAsciiCompute(getLongSafe(bytes, i), hash);
1418         }
1419         switch(remainingBytes) {
1420         case 7:
1421             return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos]))
1422                           * HASH_CODE_C2 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos + 1)))
1423                           * HASH_CODE_C1 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 3));
1424         case 6:
1425             return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos)))
1426                          * HASH_CODE_C2 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 2));
1427         case 5:
1428             return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos]))
1429                          * HASH_CODE_C2 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos + 1));
1430         case 4:
1431             return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getIntSafe(bytes, startPos));
1432         case 3:
1433             return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos]))
1434                          * HASH_CODE_C2 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos + 1));
1435         case 2:
1436             return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(getShortSafe(bytes, startPos));
1437         case 1:
1438             return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(bytes[startPos]);
1439         default:
1440             return hash;
1441         }
1442     }
1443 
1444     public static String normalizedArch() {
1445         return NORMALIZED_ARCH;
1446     }
1447 
1448     public static String normalizedOs() {
1449         return NORMALIZED_OS;
1450     }
1451 
1452     public static Set<String> normalizedLinuxClassifiers() {
1453         return LINUX_OS_CLASSIFIERS;
1454     }
1455 
1456     public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
1457         if (directory == null) {
1458             return Files.createTempFile(prefix, suffix).toFile();
1459         }
1460         return Files.createTempFile(directory.toPath(), prefix, suffix).toFile();
1461     }
1462 
1463     /**
1464      * Adds only those classifier strings to <tt>dest</tt> which are present in <tt>allowed</tt>.
1465      *
1466      * @param allowed          allowed classifiers
1467      * @param dest             destination set
1468      * @param maybeClassifiers potential classifiers to add
1469      */
1470     private static void addClassifier(Set<String> allowed, Set<String> dest, String... maybeClassifiers) {
1471         for (String id : maybeClassifiers) {
1472             if (allowed.contains(id)) {
1473                 dest.add(id);
1474             }
1475         }
1476     }
1477 
1478     private static String normalizeOsReleaseVariableValue(String value) {
1479         // Variable assignment values may be enclosed in double or single quotes.
1480         return value.trim().replaceAll("[\"']", "");
1481     }
1482 
1483     private static String normalize(String value) {
1484         return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
1485     }
1486 
1487     private static String normalizeArch(String value) {
1488         value = normalize(value);
1489         if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
1490             return "x86_64";
1491         }
1492         if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
1493             return "x86_32";
1494         }
1495         if (value.matches("^(ia64|itanium64)$")) {
1496             return "itanium_64";
1497         }
1498         if (value.matches("^(sparc|sparc32)$")) {
1499             return "sparc_32";
1500         }
1501         if (value.matches("^(sparcv9|sparc64)$")) {
1502             return "sparc_64";
1503         }
1504         if (value.matches("^(arm|arm32)$")) {
1505             return "arm_32";
1506         }
1507         if ("aarch64".equals(value)) {
1508             return "aarch_64";
1509         }
1510         if ("riscv64".equals(value)) {
1511             // os.detected.arch is riscv64 for RISC-V, no underscore
1512             return "riscv64";
1513         }
1514         if (value.matches("^(ppc|ppc32)$")) {
1515             return "ppc_32";
1516         }
1517         if ("ppc64".equals(value)) {
1518             return "ppc_64";
1519         }
1520         if ("ppc64le".equals(value)) {
1521             return "ppcle_64";
1522         }
1523         if ("s390".equals(value)) {
1524             return "s390_32";
1525         }
1526         if ("s390x".equals(value)) {
1527             return "s390_64";
1528         }
1529         if ("loongarch64".equals(value)) {
1530             return "loongarch_64";
1531         }
1532 
1533         return "unknown";
1534     }
1535 
1536     private static String normalizeOs(String value) {
1537         value = normalize(value);
1538         if (value.startsWith("aix")) {
1539             return "aix";
1540         }
1541         if (value.startsWith("hpux")) {
1542             return "hpux";
1543         }
1544         if (value.startsWith("os400")) {
1545             // Avoid the names such as os4000
1546             if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) {
1547                 return "os400";
1548             }
1549         }
1550         if (value.startsWith("linux")) {
1551             return "linux";
1552         }
1553         if (value.startsWith("macosx") || value.startsWith("osx") || value.startsWith("darwin")) {
1554             return "osx";
1555         }
1556         if (value.startsWith("freebsd")) {
1557             return "freebsd";
1558         }
1559         if (value.startsWith("openbsd")) {
1560             return "openbsd";
1561         }
1562         if (value.startsWith("netbsd")) {
1563             return "netbsd";
1564         }
1565         if (value.startsWith("solaris") || value.startsWith("sunos")) {
1566             return "sunos";
1567         }
1568         if (value.startsWith("windows")) {
1569             return "windows";
1570         }
1571 
1572         return "unknown";
1573     }
1574 
1575     private static final class AtomicLongCounter extends AtomicLong implements LongCounter {
1576         private static final long serialVersionUID = 4074772784610639305L;
1577 
1578         @Override
1579         public void add(long delta) {
1580             addAndGet(delta);
1581         }
1582 
1583         @Override
1584         public void increment() {
1585             incrementAndGet();
1586         }
1587 
1588         @Override
1589         public void decrement() {
1590             decrementAndGet();
1591         }
1592 
1593         @Override
1594         public long value() {
1595             return get();
1596         }
1597     }
1598 
1599     private interface ThreadLocalRandomProvider {
1600         Random current();
1601     }
1602 
1603     private PlatformDependent() {
1604         // only static method supported
1605     }
1606 }