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