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