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