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