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