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