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