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