View Javadoc
1   /*
2    * Copyright 2013 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 sun.misc.Unsafe;
21  
22  import java.lang.invoke.MethodHandle;
23  import java.lang.invoke.MethodHandles;
24  import java.lang.invoke.MethodType;
25  import java.lang.reflect.Constructor;
26  import java.lang.reflect.Field;
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Method;
29  import java.nio.Buffer;
30  import java.nio.ByteBuffer;
31  import java.security.AccessController;
32  import java.security.PrivilegedAction;
33  import java.util.SplittableRandom;
34  import java.util.concurrent.atomic.AtomicLong;
35  
36  import static java.lang.invoke.MethodType.methodType;
37  
38  /**
39   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
40   */
41  final class PlatformDependent0 {
42  
43      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
44      private static final long ADDRESS_FIELD_OFFSET;
45      private static final long BYTE_ARRAY_BASE_OFFSET;
46      private static final long INT_ARRAY_BASE_OFFSET;
47      private static final long INT_ARRAY_INDEX_SCALE;
48      private static final long LONG_ARRAY_BASE_OFFSET;
49      private static final long LONG_ARRAY_INDEX_SCALE;
50      private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR;
51      private static final MethodHandle ALLOCATE_ARRAY_METHOD;
52      private static final MethodHandle ALIGN_SLICE;
53      private static final MethodHandle OFFSET_SLICE;
54      private static final MethodHandle ABSOLUTE_PUT_BUFFER;
55      private static final MethodHandle ABSOLUTE_PUT_ARRAY;
56      private static final MethodHandle MEMORY_SEGMENT_ADDRESS_OF_BUFFER;
57      private static final MethodHandle SPLITTABLE_RANDOM_NEXT_BYTES;
58      private static final boolean IS_ANDROID = isAndroid0();
59      private static final int JAVA_VERSION = javaVersion0();
60      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
61  
62      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
63  
64      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
65      // ImageInfo.java
66      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
67              "org.graalvm.nativeimage.imagecode");
68  
69      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
70  
71      // Package-private for testing.
72      static final MethodHandle IS_VIRTUAL_THREAD_METHOD_HANDLE = getIsVirtualThreadMethodHandle();
73  
74      static final Unsafe UNSAFE;
75  
76      // constants borrowed from murmur3
77      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
78      static final int HASH_CODE_C1 = 0xcc9e2d51;
79      static final int HASH_CODE_C2 = 0x1b873593;
80  
81      /**
82       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
83       * during a large copy.
84       */
85      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
86  
87      private static final boolean UNALIGNED;
88  
89      private static final long BITS_MAX_DIRECT_MEMORY;
90  
91      static {
92          MethodHandles.Lookup lookup = MethodHandles.lookup();
93          final ByteBuffer direct;
94          Field addressField = null;
95          MethodHandle allocateArrayMethod = null;
96          Throwable unsafeUnavailabilityCause;
97          Unsafe unsafe;
98          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
99              direct = null;
100             addressField = null;
101             unsafe = null;
102         } else {
103             direct = ByteBuffer.allocateDirect(1);
104 
105             // attempt to access field Unsafe#theUnsafe
106             final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
107                 @Override
108                 public Object run() {
109                     try {
110                         final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
111                         // We always want to try using Unsafe as the access still works on java9 as well and
112                         // we need it for out native-transports and many optimizations.
113                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
114                         if (cause != null) {
115                             return cause;
116                         }
117                         // the unsafe instance
118                         return unsafeField.get(null);
119                     } catch (NoSuchFieldException | IllegalAccessException | SecurityException e) {
120                         return e;
121                     } catch (NoClassDefFoundError e) {
122                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
123                         // Unsafe unloadable.
124                         return e;
125                     }
126                 }
127             });
128 
129             // the conditional check here can not be replaced with checking that maybeUnsafe
130             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
131             // instanceof check against Unsafe will trigger a class load and we might not have
132             // the runtime permission accessClassInPackage.sun.misc
133             if (maybeUnsafe instanceof Throwable) {
134                 unsafe = null;
135                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
136                 if (logger.isTraceEnabled()) {
137                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", unsafeUnavailabilityCause);
138                 } else {
139                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", unsafeUnavailabilityCause.getMessage());
140                 }
141             } else {
142                 unsafe = (Unsafe) maybeUnsafe;
143                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
144             }
145 
146             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK,
147             // or that they haven't been removed by JEP 471.
148             // https://github.com/netty/netty/issues/1061
149             // https://www.mail-archive.com/[email protected]/msg00698.html
150             // https://openjdk.org/jeps/471
151             if (unsafe != null) {
152                 final Unsafe finalUnsafe = unsafe;
153                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
154                     @Override
155                     public Object run() {
156                         try {
157                             // Other methods like storeFence() and invokeCleaner() are tested for elsewhere.
158                             Class<? extends Unsafe> cls = finalUnsafe.getClass();
159                             cls.getDeclaredMethod(
160                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
161                             if (javaVersion() > 23) {
162                                 cls.getDeclaredMethod("objectFieldOffset", Field.class);
163                                 cls.getDeclaredMethod("staticFieldOffset", Field.class);
164                                 cls.getDeclaredMethod("staticFieldBase", Field.class);
165                                 cls.getDeclaredMethod("arrayBaseOffset", Class.class);
166                                 cls.getDeclaredMethod("arrayIndexScale", Class.class);
167                                 cls.getDeclaredMethod("allocateMemory", long.class);
168                                 cls.getDeclaredMethod("reallocateMemory", long.class, long.class);
169                                 cls.getDeclaredMethod("freeMemory", long.class);
170                                 cls.getDeclaredMethod("setMemory", long.class, long.class, byte.class);
171                                 cls.getDeclaredMethod("setMemory", Object.class, long.class, long.class, byte.class);
172                                 cls.getDeclaredMethod("getBoolean", Object.class, long.class);
173                                 cls.getDeclaredMethod("getByte", long.class);
174                                 cls.getDeclaredMethod("getByte", Object.class, long.class);
175                                 cls.getDeclaredMethod("getInt", long.class);
176                                 cls.getDeclaredMethod("getInt", Object.class, long.class);
177                                 cls.getDeclaredMethod("getLong", long.class);
178                                 cls.getDeclaredMethod("getLong", Object.class, long.class);
179                                 cls.getDeclaredMethod("putByte", long.class, byte.class);
180                                 cls.getDeclaredMethod("putByte", Object.class, long.class, byte.class);
181                                 cls.getDeclaredMethod("putInt", long.class, int.class);
182                                 cls.getDeclaredMethod("putInt", Object.class, long.class, int.class);
183                                 cls.getDeclaredMethod("putLong", long.class, long.class);
184                                 cls.getDeclaredMethod("putLong", Object.class, long.class, long.class);
185                                 cls.getDeclaredMethod("addressSize");
186                             }
187                             if (javaVersion() >= 23) {
188                                 // The following tests the methods are usable.
189                                 // Will throw UnsupportedOperationException if unsafe memory access is denied:
190                                 long address = finalUnsafe.allocateMemory(8);
191                                 finalUnsafe.putLong(address, 42);
192                                 finalUnsafe.freeMemory(address);
193                             }
194                             return null;
195                         } catch (UnsupportedOperationException | SecurityException | NoSuchMethodException e) {
196                             return e;
197                         }
198                     }
199                 });
200 
201                 if (maybeException == null) {
202                     logger.debug("sun.misc.Unsafe base methods: all available");
203                 } else {
204                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
205                     unsafe = null;
206                     unsafeUnavailabilityCause = (Throwable) maybeException;
207                     if (logger.isTraceEnabled()) {
208                         logger.debug("sun.misc.Unsafe method unavailable:", unsafeUnavailabilityCause);
209                     } else {
210                         logger.debug("sun.misc.Unsafe method unavailable: {}", unsafeUnavailabilityCause.getMessage());
211                     }
212                 }
213             }
214 
215             if (unsafe != null) {
216                 final Unsafe finalUnsafe = unsafe;
217 
218                 // attempt to access field Buffer#address
219                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
220                     @Override
221                     public Object run() {
222                         try {
223                             final Field field = Buffer.class.getDeclaredField("address");
224                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
225                             // will forbid changing the access level via reflection.
226                             final long offset = finalUnsafe.objectFieldOffset(field);
227                             final long address = finalUnsafe.getLong(direct, offset);
228 
229                             // if direct really is a direct buffer, address will be non-zero
230                             if (address == 0) {
231                                 return null;
232                             }
233                             return field;
234                         } catch (NoSuchFieldException | SecurityException e) {
235                             return e;
236                         }
237                     }
238                 });
239 
240                 if (maybeAddressField instanceof Field) {
241                     addressField = (Field) maybeAddressField;
242                     logger.debug("java.nio.Buffer.address: available");
243                 } else {
244                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
245                     if (logger.isTraceEnabled()) {
246                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
247                     } else {
248                         logger.debug("java.nio.Buffer.address: unavailable: {}",
249                                 ((Throwable) maybeAddressField).getMessage());
250                     }
251 
252                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
253                     // Let's just pretend unsafe is unavailable for overall simplicity.
254                     unsafe = null;
255                 }
256             }
257 
258             if (unsafe != null) {
259                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
260                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
261                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
262                 if (byteArrayIndexScale != 1) {
263                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
264                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
265                     unsafe = null;
266                 }
267             }
268         }
269         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
270         UNSAFE = unsafe;
271 
272         if (unsafe == null) {
273             ADDRESS_FIELD_OFFSET = -1;
274             BYTE_ARRAY_BASE_OFFSET = -1;
275             LONG_ARRAY_BASE_OFFSET = -1;
276             LONG_ARRAY_INDEX_SCALE = -1;
277             INT_ARRAY_BASE_OFFSET = -1;
278             INT_ARRAY_INDEX_SCALE = -1;
279             UNALIGNED = false;
280             BITS_MAX_DIRECT_MEMORY = -1;
281             DIRECT_BUFFER_CONSTRUCTOR = null;
282             ALLOCATE_ARRAY_METHOD = null;
283         } else {
284             MethodHandle directBufferConstructor;
285             long address = -1;
286             try {
287                 final Object maybeDirectBufferConstructor =
288                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
289                             @Override
290                             public Object run() {
291                                 try {
292                                     Class<? extends ByteBuffer> directClass = direct.getClass();
293                                     final Constructor<?> constructor = javaVersion() >= 21 ?
294                                             directClass.getDeclaredConstructor(long.class, long.class) :
295                                             directClass.getDeclaredConstructor(long.class, int.class);
296                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
297                                     if (cause != null) {
298                                         return cause;
299                                     }
300                                     return lookup.unreflectConstructor(constructor)
301                                             .asType(methodType(ByteBuffer.class, long.class, int.class));
302                                 } catch (Throwable e) {
303                                     return e;
304                                 }
305                             }
306                         });
307 
308                 if (maybeDirectBufferConstructor instanceof MethodHandle) {
309                     address = UNSAFE.allocateMemory(1);
310                     // try to use the constructor now
311                     try {
312                         MethodHandle constructor = (MethodHandle) maybeDirectBufferConstructor;
313                         ByteBuffer ignore = (ByteBuffer) constructor.invokeExact(address, 1);
314                         directBufferConstructor = constructor;
315                         logger.debug("direct buffer constructor: available");
316                     } catch (Throwable e) {
317                         directBufferConstructor = null;
318                     }
319                 } else {
320                     if (logger.isTraceEnabled()) {
321                         logger.debug("direct buffer constructor: unavailable",
322                                 (Throwable) maybeDirectBufferConstructor);
323                     } else {
324                         logger.debug("direct buffer constructor: unavailable: {}",
325                                 ((Throwable) maybeDirectBufferConstructor).getMessage());
326                     }
327                     directBufferConstructor = null;
328                 }
329             } finally {
330                 if (address != -1) {
331                     UNSAFE.freeMemory(address);
332                 }
333             }
334             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
335             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
336             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
337             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
338             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
339             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
340             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
341             final boolean unaligned;
342             String unalignedProperty = SystemPropertyUtil.get("io.netty.unalignedAccess", "").trim();
343 
344             // using a known type to avoid loading new classes
345             final AtomicLong maybeMaxMemory = new AtomicLong(-1);
346             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
347                 @Override
348                 public Object run() {
349                     if ("true".equalsIgnoreCase(unalignedProperty)) {
350                         return Boolean.TRUE;
351                     }
352                     if ("false".equalsIgnoreCase(unalignedProperty)) {
353                         return Boolean.FALSE;
354                     }
355                     try {
356                         Class<?> bitsClass =
357                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
358                         int version = javaVersion();
359                         if (version >= 9) {
360                             // Java9/10 use all lowercase and later versions all uppercase.
361                             String fieldName = version >= 11? "MAX_MEMORY" : "maxMemory";
362                             // On Java9 and later we try to directly access the field as we can do this without
363                             // adjust the accessible levels.
364                             try {
365                                 Field maxMemoryField = bitsClass.getDeclaredField(fieldName);
366                                 if (maxMemoryField.getType() == long.class) {
367                                     long offset = UNSAFE.staticFieldOffset(maxMemoryField);
368                                     Object object = UNSAFE.staticFieldBase(maxMemoryField);
369                                     maybeMaxMemory.lazySet(UNSAFE.getLong(object, offset));
370                                 }
371                             } catch (Throwable ignore) {
372                                 // ignore if can't access
373                             }
374                             fieldName = version >= 11? "UNALIGNED" : "unaligned";
375                             try {
376                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
377                                 if (unalignedField.getType() == boolean.class) {
378                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
379                                     Object object = UNSAFE.staticFieldBase(unalignedField);
380                                     return UNSAFE.getBoolean(object, offset);
381                                 }
382                                 // There is something unexpected stored in the field,
383                                 // let us fall-back and try to use a reflective method call as last resort.
384                             } catch (NoSuchFieldException ignore) {
385                                 // We did not find the field we expected, move on.
386                             }
387                         }
388                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
389                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
390                         if (cause != null) {
391                             return cause;
392                         }
393                         return unalignedMethod.invoke(null);
394                     } catch (NoSuchMethodException | SecurityException | IllegalAccessException |
395                              InvocationTargetException | ClassNotFoundException e) {
396                         return e;
397                     }
398                 }
399             });
400 
401             if (maybeUnaligned instanceof Boolean) {
402                 unaligned = (Boolean) maybeUnaligned;
403                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
404             } else {
405                 String arch = SystemPropertyUtil.get("os.arch", "");
406                 //noinspection DynamicRegexReplaceableByCompiledPattern
407                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
408                 Throwable t = (Throwable) maybeUnaligned;
409                 if (logger.isTraceEnabled()) {
410                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
411                 } else {
412                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
413                 }
414             }
415 
416             UNALIGNED = unaligned;
417             BITS_MAX_DIRECT_MEMORY = maybeMaxMemory.get() >= 0? maybeMaxMemory.get() : -1;
418 
419             if (javaVersion() >= 9) {
420                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
421                     @Override
422                     public Object run() {
423                         try {
424                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
425                             // sun.misc.Unsafe
426                             Class<?> cls = getClassLoader(PlatformDependent0.class)
427                                     .loadClass("jdk.internal.misc.Unsafe");
428                             return lookup.findStatic(cls, "getUnsafe", methodType(cls)).invoke();
429                         } catch (Throwable e) {
430                             return e;
431                         }
432                     }
433                 });
434                 if (!(maybeException instanceof Throwable)) {
435                     final Object finalInternalUnsafe = maybeException;
436                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
437                         @Override
438                         public Object run() {
439                             try {
440                                 Class<?> finalInternalUnsafeClass = finalInternalUnsafe.getClass();
441                                 return lookup.findVirtual(
442                                         finalInternalUnsafeClass,
443                                         "allocateUninitializedArray",
444                                         methodType(Object.class, Class.class, int.class));
445                             } catch (Throwable e) {
446                                 return e;
447                             }
448                         }
449                     });
450 
451                     if (maybeException instanceof MethodHandle) {
452                         try {
453                             MethodHandle m = (MethodHandle) maybeException;
454                             m = m.bindTo(finalInternalUnsafe);
455                             byte[] bytes = (byte[]) (Object) m.invokeExact(byte.class, 8);
456                             assert bytes.length == 8;
457                             allocateArrayMethod = m;
458                         } catch (Throwable e) {
459                             maybeException = e;
460                         }
461                     }
462                 }
463 
464                 if (maybeException instanceof Throwable) {
465                     if (logger.isTraceEnabled()) {
466                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
467                                 (Throwable) maybeException);
468                     } else {
469                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
470                                 ((Throwable) maybeException).getMessage());
471                     }
472                 } else {
473                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
474                 }
475             } else {
476                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
477             }
478             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
479         }
480 
481         if (javaVersion() > 9) {
482             ALIGN_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
483                 @Override
484                 public Object run() {
485                     try {
486                         return MethodHandles.publicLookup().findVirtual(
487                                 ByteBuffer.class, "alignedSlice", methodType(ByteBuffer.class, int.class));
488                     } catch (Throwable e) {
489                         return null;
490                     }
491                 }
492             });
493         } else {
494             ALIGN_SLICE = null;
495         }
496 
497         if (javaVersion() >= 13) {
498             OFFSET_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
499                 @Override
500                 public Object run() {
501                     try {
502                         return MethodHandles.publicLookup().findVirtual(
503                                 ByteBuffer.class, "slice", methodType(ByteBuffer.class, int.class, int.class));
504                     } catch (Throwable e) {
505                         return null;
506                     }
507                 }
508             });
509         } else {
510             OFFSET_SLICE = null;
511         }
512 
513         if (javaVersion() >= 16) {
514             ABSOLUTE_PUT_BUFFER = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
515                 @Override
516                 public Object run() {
517                     try {
518                         MethodType type =
519                                 methodType(ByteBuffer.class, int.class, ByteBuffer.class, int.class, int.class);
520                         return MethodHandles.publicLookup().findVirtual(ByteBuffer.class, "put", type);
521                     } catch (Throwable e) {
522                         return null;
523                     }
524                 }
525             });
526         } else {
527             ABSOLUTE_PUT_BUFFER = null;
528         }
529 
530         if (javaVersion() >= 13) {
531             ABSOLUTE_PUT_ARRAY = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
532                 @Override
533                 public Object run() {
534                     try {
535                         MethodType type =
536                                 methodType(ByteBuffer.class, int.class, byte[].class, int.class, int.class);
537                         return MethodHandles.publicLookup().findVirtual(ByteBuffer.class, "put", type);
538                     } catch (Throwable e) {
539                         return null;
540                     }
541                 }
542             });
543         } else {
544             ABSOLUTE_PUT_ARRAY = null;
545         }
546         if (javaVersion() >= 22) {
547             MEMORY_SEGMENT_ADDRESS_OF_BUFFER = (MethodHandle) AccessController.doPrivileged(
548                     new PrivilegedAction<Object>() {
549                 @Override
550                 public Object run() {
551                     try {
552                         // We're recreating the following code snippet:
553                         // (long) MemorySegment.ofBuffer((Buffer) arg1).address();
554                         Class<?> memsegCls = Class.forName("java.lang.foreign.MemorySegment");
555                         MethodType ofBufferType = methodType(memsegCls, Buffer.class);
556                         MethodType addressType = methodType(long.class);
557                         MethodHandles.Lookup lookup = MethodHandles.publicLookup();
558                         MethodHandle ofBuffer = lookup.findStatic(memsegCls, "ofBuffer", ofBufferType);
559                         MethodHandle address = lookup.findVirtual(memsegCls, "address", addressType);
560                         return MethodHandles.filterArguments(address, 0, ofBuffer);
561                     } catch (Throwable e) {
562                         return null;
563                     }
564                 }
565             });
566         } else {
567             MEMORY_SEGMENT_ADDRESS_OF_BUFFER = null;
568         }
569 
570         if (javaVersion() >= 10) {
571             SPLITTABLE_RANDOM_NEXT_BYTES = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
572                 @Override
573                 public Object run() {
574                     try {
575                         MethodType type = methodType(void.class, byte[].class);
576                         Class<SplittableRandom> cls = SplittableRandom.class;
577                         return MethodHandles.publicLookup().findVirtual(cls, "nextBytes", type);
578                     } catch (Exception e) {
579                         return null;
580                     }
581                 }
582             });
583         } else {
584             SPLITTABLE_RANDOM_NEXT_BYTES = null;
585         }
586 
587         logger.debug("java.nio.DirectByteBuffer.<init>(long, {int,long}): {}",
588                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
589     }
590 
591     private static MethodHandle getIsVirtualThreadMethodHandle() {
592         try {
593             MethodHandle methodHandle = MethodHandles.publicLookup().findVirtual(Thread.class, "isVirtual",
594                     methodType(boolean.class));
595             // Call once to make sure the invocation works.
596             boolean isVirtual = (boolean) methodHandle.invokeExact(Thread.currentThread());
597             return methodHandle;
598         } catch (Throwable e) {
599             if (logger.isTraceEnabled()) {
600                 logger.debug("Thread.isVirtual() is not available: ", e);
601             } else {
602                 logger.debug("Thread.isVirtual() is not available: ", e.getMessage());
603             }
604             return null;
605         }
606     }
607 
608     /**
609      * @param thread The thread to be checked.
610      * @return {@code true} if this {@link Thread} is a virtual thread, {@code false} otherwise.
611      */
612     static boolean isVirtualThread(Thread thread) {
613         if (thread == null || IS_VIRTUAL_THREAD_METHOD_HANDLE == null) {
614             return false;
615         }
616         try {
617             return (boolean) IS_VIRTUAL_THREAD_METHOD_HANDLE.invokeExact(thread);
618         } catch (Throwable t) {
619             // Should not happen.
620             if (t instanceof Error) {
621                 throw (Error) t;
622             }
623             throw new Error(t);
624         }
625     }
626 
627     static boolean isNativeImage() {
628         return RUNNING_IN_NATIVE_IMAGE;
629     }
630 
631     static boolean isExplicitNoUnsafe() {
632         return EXPLICIT_NO_UNSAFE_CAUSE != null;
633     }
634 
635     private static Throwable explicitNoUnsafeCause0() {
636         boolean explicitProperty = SystemPropertyUtil.contains("io.netty.noUnsafe");
637         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
638         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
639 
640         // See JDK 23 JEP 471 https://openjdk.org/jeps/471 and sun.misc.Unsafe.beforeMemoryAccess() on JDK 23+.
641         // And JDK 24 JEP 498 https://openjdk.org/jeps/498, that enable warnings by default.
642         // Due to JDK bugs, we only actually disable Unsafe by default on Java 25+, where we have memory segment APIs
643         // available, and working.
644         String reason = "io.netty.noUnsafe";
645         String unspecified = "<unspecified>";
646         String unsafeMemoryAccess = SystemPropertyUtil.get("sun.misc.unsafe.memory.access", unspecified);
647         if (!explicitProperty && unspecified.equals(unsafeMemoryAccess) && javaVersion() >= 25) {
648             reason = "io.netty.noUnsafe=true by default on Java 25+";
649             noUnsafe = true;
650         } else if (!("allow".equals(unsafeMemoryAccess) || unspecified.equals(unsafeMemoryAccess))) {
651             reason = "--sun-misc-unsafe-memory-access=" + unsafeMemoryAccess;
652             noUnsafe = true;
653         }
654 
655         if (noUnsafe) {
656             String msg = "sun.misc.Unsafe: unavailable (" + reason + ')';
657             logger.debug(msg);
658             return new UnsupportedOperationException(msg);
659         }
660 
661         // Legacy properties
662         String unsafePropName;
663         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
664             unsafePropName = "io.netty.tryUnsafe";
665         } else {
666             unsafePropName = "org.jboss.netty.tryUnsafe";
667         }
668 
669         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
670             String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ')';
671             logger.debug(msg);
672             return new UnsupportedOperationException(msg);
673         }
674 
675         return null;
676     }
677 
678     static boolean isUnaligned() {
679         return UNALIGNED;
680     }
681 
682     /**
683      * Any value >= 0 should be considered as a valid max direct memory value.
684      */
685     static long bitsMaxDirectMemory() {
686         return BITS_MAX_DIRECT_MEMORY;
687     }
688 
689     static boolean hasUnsafe() {
690         return UNSAFE != null;
691     }
692 
693     static Throwable getUnsafeUnavailabilityCause() {
694         return UNSAFE_UNAVAILABILITY_CAUSE;
695     }
696 
697     static boolean hasMemorySegmentAddressOfBuffer() {
698         return MEMORY_SEGMENT_ADDRESS_OF_BUFFER != null;
699     }
700 
701     static boolean unalignedAccess() {
702         return UNALIGNED;
703     }
704 
705     static void splittableRandomNextBytes(SplittableRandom rng, byte[] data) {
706         try {
707             SPLITTABLE_RANDOM_NEXT_BYTES.invokeExact(rng, data);
708         } catch (Throwable e) {
709             throw new LinkageError("Error calling SplittableRandom.nextBytes", e);
710         }
711     }
712 
713     static void throwException(Throwable cause) {
714         throwException0(cause);
715     }
716 
717     @SuppressWarnings("unchecked")
718     private static <E extends Throwable> void throwException0(Throwable t) throws E {
719         throw (E) t;
720     }
721 
722     static boolean hasDirectBufferNoCleanerConstructor() {
723         return DIRECT_BUFFER_CONSTRUCTOR != null;
724     }
725 
726     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
727         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
728     }
729 
730     static ByteBuffer allocateDirectNoCleaner(int capacity) {
731         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
732         // Just use 1 to make it safe to use in all cases:
733         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
734         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity);
735     }
736 
737     static boolean hasAlignSliceMethod() {
738         return ALIGN_SLICE != null;
739     }
740 
741     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
742         try {
743             return (ByteBuffer) ALIGN_SLICE.invokeExact(buffer, alignment);
744         } catch (Throwable e) {
745             rethrowIfPossible(e);
746             throw new LinkageError("ByteBuffer.alignedSlice not available", e);
747         }
748     }
749 
750     static boolean hasOffsetSliceMethod() {
751         return OFFSET_SLICE != null;
752     }
753 
754     static ByteBuffer offsetSlice(ByteBuffer buffer, int index, int length) {
755         try {
756             return (ByteBuffer) OFFSET_SLICE.invokeExact(buffer, index, length);
757         } catch (Throwable e) {
758             rethrowIfPossible(e);
759             throw new LinkageError("ByteBuffer.slice(int, int) not available", e);
760         }
761     }
762 
763     static boolean hasAbsolutePutBufferMethod() {
764         return ABSOLUTE_PUT_BUFFER != null;
765     }
766 
767     static boolean hasAbsolutePutArrayMethod() {
768         return ABSOLUTE_PUT_ARRAY != null;
769     }
770 
771     static ByteBuffer absolutePut(ByteBuffer dst, int dstOffset, ByteBuffer src, int srcOffset, int length) {
772         try {
773             return (ByteBuffer) ABSOLUTE_PUT_BUFFER.invokeExact(dst, dstOffset, src, srcOffset, length);
774         } catch (Throwable e) {
775             rethrowIfPossible(e);
776             throw new LinkageError("ByteBuffer.put(int, ByteBuffer, int, int) not available", e);
777         }
778     }
779 
780     static ByteBuffer absolutePut(ByteBuffer dst, int dstOffset, byte[] src, int srcOffset, int length) {
781         try {
782             return (ByteBuffer) ABSOLUTE_PUT_ARRAY.invokeExact(dst, dstOffset, src, srcOffset, length);
783         } catch (Throwable e) {
784             rethrowIfPossible(e);
785             throw new LinkageError("ByteBuffer.put(int, byte[], int, int) not available", e);
786         }
787     }
788 
789     static boolean hasAllocateArrayMethod() {
790         return ALLOCATE_ARRAY_METHOD != null;
791     }
792 
793     static byte[] allocateUninitializedArray(int size) {
794         try {
795             return (byte[]) (Object) ALLOCATE_ARRAY_METHOD.invokeExact(byte.class, size);
796         } catch (Throwable e) {
797             rethrowIfPossible(e);
798             throw new LinkageError("Unsafe.allocateUninitializedArray not available", e);
799         }
800     }
801 
802     static ByteBuffer newDirectBuffer(long address, int capacity) {
803         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
804 
805         try {
806             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.invokeExact(address, capacity);
807         } catch (Throwable cause) {
808             rethrowIfPossible(cause);
809             throw new LinkageError("DirectByteBuffer constructor not available", cause);
810         }
811     }
812 
813     private static void rethrowIfPossible(Throwable cause) {
814         if (cause instanceof Error) {
815             throw (Error) cause;
816         }
817         if (cause instanceof RuntimeException) {
818             throw (RuntimeException) cause;
819         }
820     }
821 
822     static boolean hasDirectByteBufferAddress(ByteBuffer buffer) {
823         return buffer.isDirect() && (hasUnsafe() || hasMemorySegmentAddressOfBuffer());
824     }
825 
826     static long directBufferAddress(ByteBuffer buffer) {
827         if (hasUnsafe()) {
828             return getLong(buffer, ADDRESS_FIELD_OFFSET);
829         }
830         if (hasMemorySegmentAddressOfBuffer()) {
831             try {
832                 // MemorySegment.ofBuffer(buffer).address() includes the current position offset.
833                 // Netty/JNI GetDirectBufferAddress expects the base (index 0) address, so subtract position.
834                 return (long) MEMORY_SEGMENT_ADDRESS_OF_BUFFER.invokeExact((Buffer) buffer) - buffer.position();
835             } catch (Throwable e) {
836                 LinkageError error = new LinkageError("Failed to call MemorySegment.ofBuffer(arg1).address()");
837                 error.initCause(e);
838                 throw error;
839             }
840         }
841         throw new IllegalStateException("No address access method");
842     }
843 
844     static long byteArrayBaseOffset() {
845         return BYTE_ARRAY_BASE_OFFSET;
846     }
847 
848     static Object getObject(Object object, long fieldOffset) {
849         return UNSAFE.getObject(object, fieldOffset);
850     }
851 
852     static int getInt(Object object, long fieldOffset) {
853         return UNSAFE.getInt(object, fieldOffset);
854     }
855 
856     static int getIntVolatile(Object object, long fieldOffset) {
857         return UNSAFE.getIntVolatile(object, fieldOffset);
858     }
859 
860     static void putOrderedInt(Object object, long fieldOffset, int value) {
861         UNSAFE.putOrderedInt(object, fieldOffset, value);
862     }
863 
864     static int getAndAddInt(Object object, long fieldOffset, int value) {
865         return UNSAFE.getAndAddInt(object, fieldOffset, value);
866     }
867 
868     static boolean compareAndSwapInt(Object object, long fieldOffset, int expected, int value) {
869         return UNSAFE.compareAndSwapInt(object, fieldOffset, expected, value);
870     }
871 
872     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
873         UNSAFE.putInt(object, fieldOffset, value);
874         UNSAFE.storeFence();
875     }
876 
877     private static long getLong(Object object, long fieldOffset) {
878         return UNSAFE.getLong(object, fieldOffset);
879     }
880 
881     static long objectFieldOffset(Field field) {
882         return UNSAFE.objectFieldOffset(field);
883     }
884 
885     static byte getByte(long address) {
886         return UNSAFE.getByte(address);
887     }
888 
889     static short getShort(long address) {
890         return UNSAFE.getShort(address);
891     }
892 
893     static int getInt(long address) {
894         return UNSAFE.getInt(address);
895     }
896 
897     static long getLong(long address) {
898         return UNSAFE.getLong(address);
899     }
900 
901     static byte getByte(byte[] data, int index) {
902         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
903     }
904 
905     static byte getByte(byte[] data, long index) {
906         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
907     }
908 
909     static short getShort(byte[] data, int index) {
910         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
911     }
912 
913     static int getInt(byte[] data, int index) {
914         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
915     }
916 
917     static int getInt(int[] data, long index) {
918         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
919     }
920 
921     static long getLong(byte[] data, int index) {
922         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
923     }
924 
925     static long getLong(long[] data, long index) {
926         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
927     }
928 
929     static void putByte(long address, byte value) {
930         UNSAFE.putByte(address, value);
931     }
932 
933     static void putShort(long address, short value) {
934         UNSAFE.putShort(address, value);
935     }
936 
937     static void putShortOrdered(long address, short newValue) {
938         UNSAFE.storeFence();
939         UNSAFE.putShort(null, address, newValue);
940     }
941 
942     static void putInt(long address, int value) {
943         UNSAFE.putInt(address, value);
944     }
945 
946     static void putLong(long address, long value) {
947         UNSAFE.putLong(address, value);
948     }
949 
950     static void putByte(byte[] data, int index, byte value) {
951         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
952     }
953 
954     static void putByte(Object data, long offset, byte value) {
955         UNSAFE.putByte(data, offset, value);
956     }
957 
958     static void putShort(byte[] data, int index, short value) {
959         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
960     }
961 
962     static void putInt(byte[] data, int index, int value) {
963         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
964     }
965 
966     static void putLong(byte[] data, int index, long value) {
967         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
968     }
969 
970     static void putObject(Object o, long offset, Object x) {
971         UNSAFE.putObject(o, offset, x);
972     }
973 
974     static void copyMemory(long srcAddr, long dstAddr, long length) {
975         // Manual safe-point polling is only needed prior Java9:
976         // See https://bugs.openjdk.java.net/browse/JDK-8149596
977         if (javaVersion() <= 8) {
978             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
979         } else {
980             UNSAFE.copyMemory(srcAddr, dstAddr, length);
981         }
982     }
983 
984     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
985         while (length > 0) {
986             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
987             UNSAFE.copyMemory(srcAddr, dstAddr, size);
988             length -= size;
989             srcAddr += size;
990             dstAddr += size;
991         }
992     }
993 
994     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
995         // Manual safe-point polling is only needed prior Java9:
996         // See https://bugs.openjdk.java.net/browse/JDK-8149596
997         if (javaVersion() <= 8) {
998             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
999         } else {
1000             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
1001         }
1002     }
1003 
1004     private static void copyMemoryWithSafePointPolling(
1005             Object src, long srcOffset, Object dst, long dstOffset, long length) {
1006         while (length > 0) {
1007             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
1008             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
1009             length -= size;
1010             srcOffset += size;
1011             dstOffset += size;
1012         }
1013     }
1014 
1015     static void setMemory(long address, long bytes, byte value) {
1016         UNSAFE.setMemory(address, bytes, value);
1017     }
1018 
1019     static void setMemory(Object o, long offset, long bytes, byte value) {
1020         UNSAFE.setMemory(o, offset, bytes, value);
1021     }
1022 
1023     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
1024         int remainingBytes = length & 7;
1025         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
1026         final long diff = startPos2 - startPos1;
1027         if (length >= 8) {
1028             final long end = baseOffset1 + remainingBytes;
1029             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
1030                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
1031                     return false;
1032                 }
1033             }
1034         }
1035         if (remainingBytes >= 4) {
1036             remainingBytes -= 4;
1037             long pos = baseOffset1 + remainingBytes;
1038             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
1039                 return false;
1040             }
1041         }
1042         final long baseOffset2 = baseOffset1 + diff;
1043         if (remainingBytes >= 2) {
1044             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
1045                     (remainingBytes == 2 ||
1046                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
1047         }
1048         return remainingBytes == 0 ||
1049                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
1050     }
1051 
1052     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
1053         long result = 0;
1054         long remainingBytes = length & 7;
1055         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
1056         final long end = baseOffset1 + remainingBytes;
1057         final long diff = startPos2 - startPos1;
1058         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
1059             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
1060         }
1061         if (remainingBytes >= 4) {
1062             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
1063             remainingBytes -= 4;
1064         }
1065         if (remainingBytes >= 2) {
1066             long pos = end - remainingBytes;
1067             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
1068             remainingBytes -= 2;
1069         }
1070         if (remainingBytes == 1) {
1071             long pos = end - 1;
1072             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
1073         }
1074         return ConstantTimeUtils.equalsConstantTime(result, 0);
1075     }
1076 
1077     static boolean isZero(byte[] bytes, int startPos, int length) {
1078         if (length <= 0) {
1079             return true;
1080         }
1081         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
1082         int remainingBytes = length & 7;
1083         final long end = baseOffset + remainingBytes;
1084         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
1085             if (UNSAFE.getLong(bytes, i) != 0) {
1086                 return false;
1087             }
1088         }
1089 
1090         if (remainingBytes >= 4) {
1091             remainingBytes -= 4;
1092             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
1093                 return false;
1094             }
1095         }
1096         if (remainingBytes >= 2) {
1097             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
1098                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
1099         }
1100         return bytes[startPos] == 0;
1101     }
1102 
1103     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
1104         int hash = HASH_CODE_ASCII_SEED;
1105         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
1106         final int remainingBytes = length & 7;
1107         final long end = baseOffset + remainingBytes;
1108         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
1109             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
1110         }
1111         if (remainingBytes == 0) {
1112             return hash;
1113         }
1114         int hcConst = HASH_CODE_C1;
1115         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
1116             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
1117             hcConst = HASH_CODE_C2;
1118             baseOffset++;
1119         }
1120         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
1121             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
1122             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
1123             baseOffset += 2;
1124         }
1125         if (remainingBytes >= 4) { // 4, 5, 6, 7
1126             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
1127         }
1128         return hash;
1129     }
1130 
1131     static int hashCodeAsciiCompute(long value, int hash) {
1132         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
1133         // code the same regardless of character case (upper case or lower case hash is the same).
1134         return hash * HASH_CODE_C1 +
1135                 // Low order int
1136                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
1137                 // High order int
1138                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
1139     }
1140 
1141     static int hashCodeAsciiSanitize(int value) {
1142         return value & 0x1f1f1f1f;
1143     }
1144 
1145     static int hashCodeAsciiSanitize(short value) {
1146         return value & 0x1f1f;
1147     }
1148 
1149     static int hashCodeAsciiSanitize(byte value) {
1150         return value & 0x1f;
1151     }
1152 
1153     static ClassLoader getClassLoader(final Class<?> clazz) {
1154         if (System.getSecurityManager() == null) {
1155             return clazz.getClassLoader();
1156         } else {
1157             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1158                 @Override
1159                 public ClassLoader run() {
1160                     return clazz.getClassLoader();
1161                 }
1162             });
1163         }
1164     }
1165 
1166     static ClassLoader getContextClassLoader() {
1167         if (System.getSecurityManager() == null) {
1168             return Thread.currentThread().getContextClassLoader();
1169         } else {
1170             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1171                 @Override
1172                 public ClassLoader run() {
1173                     return Thread.currentThread().getContextClassLoader();
1174                 }
1175             });
1176         }
1177     }
1178 
1179     static ClassLoader getSystemClassLoader() {
1180         if (System.getSecurityManager() == null) {
1181             return ClassLoader.getSystemClassLoader();
1182         } else {
1183             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1184                 @Override
1185                 public ClassLoader run() {
1186                     return ClassLoader.getSystemClassLoader();
1187                 }
1188             });
1189         }
1190     }
1191 
1192     static int addressSize() {
1193         return UNSAFE.addressSize();
1194     }
1195 
1196     static long allocateMemory(long size) {
1197         return UNSAFE.allocateMemory(size);
1198     }
1199 
1200     static void freeMemory(long address) {
1201         UNSAFE.freeMemory(address);
1202     }
1203 
1204     static long reallocateMemory(long address, long newSize) {
1205         return UNSAFE.reallocateMemory(address, newSize);
1206     }
1207 
1208     static boolean isAndroid() {
1209         return IS_ANDROID;
1210     }
1211 
1212     private static boolean isAndroid0() {
1213         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
1214         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
1215 
1216         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
1217         // OpenJDK is used, which means `Unsafe` will actually work as expected.
1218 
1219         // Android sets this property to Dalvik, regardless of whether it actually is.
1220         String vmName = SystemPropertyUtil.get("java.vm.name");
1221         boolean isAndroid = "Dalvik".equals(vmName);
1222         if (isAndroid) {
1223             logger.debug("Platform: Android");
1224         }
1225         return isAndroid;
1226     }
1227 
1228     private static boolean explicitTryReflectionSetAccessible0() {
1229         // we disable reflective access
1230         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible",
1231                 javaVersion() < 9 || RUNNING_IN_NATIVE_IMAGE);
1232     }
1233 
1234     static boolean isExplicitTryReflectionSetAccessible() {
1235         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
1236     }
1237 
1238     static int javaVersion() {
1239         return JAVA_VERSION;
1240     }
1241 
1242     private static int javaVersion0() {
1243         final int majorVersion;
1244 
1245         if (isAndroid()) {
1246             majorVersion = 6;
1247         } else {
1248             majorVersion = majorVersionFromJavaSpecificationVersion();
1249         }
1250 
1251         logger.debug("Java version: {}", majorVersion);
1252 
1253         return majorVersion;
1254     }
1255 
1256     // Package-private for testing only
1257     static int majorVersionFromJavaSpecificationVersion() {
1258         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
1259     }
1260 
1261     // Package-private for testing only
1262     static int majorVersion(final String javaSpecVersion) {
1263         final String[] components = javaSpecVersion.split("\\.");
1264         final int[] version = new int[components.length];
1265         for (int i = 0; i < components.length; i++) {
1266             version[i] = Integer.parseInt(components[i]);
1267         }
1268 
1269         if (version[0] == 1) {
1270             assert version[1] >= 6;
1271             return version[1];
1272         } else {
1273             return version[0];
1274         }
1275     }
1276 
1277     private PlatformDependent0() {
1278     }
1279 }