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.netty5.util.internal;
17  
18  import io.netty5.util.internal.logging.InternalLogger;
19  import io.netty5.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.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Parameter;
29  import java.nio.Buffer;
30  import java.nio.ByteBuffer;
31  import java.security.AccessController;
32  import java.security.PrivilegedAction;
33  import java.util.Arrays;
34  import java.util.NoSuchElementException;
35  
36  /**
37   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
38   */
39  final class PlatformDependent0 {
40  
41      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
42      private static final long ADDRESS_FIELD_OFFSET;
43      private static final long BYTE_ARRAY_BASE_OFFSET;
44      private static final long INT_ARRAY_BASE_OFFSET;
45      private static final long INT_ARRAY_INDEX_SCALE;
46      private static final long LONG_ARRAY_BASE_OFFSET;
47      private static final long LONG_ARRAY_INDEX_SCALE;
48      private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR_HANDLE;
49      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
50      private static final MethodHandle ALLOCATE_ARRAY_HANDLE;
51      private static final Method ALIGN_SLICE;
52      private static final int JAVA_VERSION = javaVersion0();
53      private static final boolean IS_ANDROID = isAndroid0();
54  
55      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
56      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
57  
58      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
59      // ImageInfo.java
60      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
61              "org.graalvm.nativeimage.imagecode");
62  
63      static final Unsafe UNSAFE;
64  
65      // constants borrowed from murmur3
66      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
67      static final int HASH_CODE_C1 = 0xcc9e2d51;
68      static final int HASH_CODE_C2 = 0x1b873593;
69  
70      /**
71       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
72       * during a large copy.
73       */
74      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
75  
76      private static final boolean UNALIGNED;
77  
78      static {
79          final ByteBuffer direct;
80          Field addressField = null;
81          MethodHandle allocateArrayHandle = null;
82          Throwable unsafeUnavailabilityCause;
83          Unsafe unsafe;
84  
85          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
86              direct = null;
87              addressField = null;
88              unsafe = null;
89          } else {
90              direct = ByteBuffer.allocateDirect(1);
91  
92              // attempt to access field Unsafe#theUnsafe
93              final Object maybeUnsafe = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
94                  try {
95                      final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
96                      // We always want to try using Unsafe as the access still works on java9 as well and
97                      // we need it for out native-transports and many optimizations.
98                      Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
99                      if (cause != null) {
100                         return cause;
101                     }
102                     // the unsafe instance
103                     return unsafeField.get(null);
104                 } catch (NoSuchFieldException | SecurityException
105                         | IllegalAccessException | NoClassDefFoundError e) {
106                     return e;
107                 } // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
108                 // Unsafe unloadable.
109             });
110 
111             // the conditional check here can not be replaced with checking that maybeUnsafe
112             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
113             // instanceof check against Unsafe will trigger a class load and we might not have
114             // the runtime permission accessClassInPackage.sun.misc
115             if (maybeUnsafe instanceof Throwable) {
116                 unsafe = null;
117                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
118                 if (logger.isTraceEnabled()) {
119                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
120                 } else {
121                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", ((Throwable) maybeUnsafe).getMessage());
122                 }
123             } else {
124                 unsafe = (Unsafe) maybeUnsafe;
125                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
126             }
127 
128             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK
129             // https://github.com/netty/netty/issues/1061
130             // https://www.mail-archive.com/[email protected]/msg00698.html
131             if (unsafe != null) {
132                 final Unsafe finalUnsafe = unsafe;
133                 final Object maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
134                     try {
135                         finalUnsafe.getClass().getDeclaredMethod(
136                                 "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
137                         return null;
138                     } catch (NoSuchMethodException | SecurityException e) {
139                         return e;
140                     }
141                 });
142 
143                 if (maybeException == null) {
144                     logger.debug("sun.misc.Unsafe.copyMemory: available");
145                 } else {
146                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
147                     unsafe = null;
148                     unsafeUnavailabilityCause = (Throwable) maybeException;
149                     if (logger.isTraceEnabled()) {
150                         logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) maybeException);
151                     } else {
152                         logger.debug("sun.misc.Unsafe.copyMemory: unavailable: {}",
153                                 ((Throwable) maybeException).getMessage());
154                     }
155                 }
156             }
157 
158             if (unsafe != null) {
159                 final Unsafe finalUnsafe = unsafe;
160 
161                 // attempt to access field Buffer#address
162                 final Object maybeAddressField = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
163                     try {
164                         final Field field = Buffer.class.getDeclaredField("address");
165                         // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
166                         // will forbid changing the access level via reflection.
167                         final long offset = finalUnsafe.objectFieldOffset(field);
168                         final long address = finalUnsafe.getLong(direct, offset);
169 
170                         // if direct really is a direct buffer, address will be non-zero
171                         if (address == 0) {
172                             return null;
173                         }
174                         return field;
175                     } catch (NoSuchFieldException | SecurityException e) {
176                         return e;
177                     }
178                 });
179 
180                 if (maybeAddressField instanceof Field) {
181                     addressField = (Field) maybeAddressField;
182                     logger.debug("java.nio.Buffer.address: available");
183                 } else {
184                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
185                     if (logger.isTraceEnabled()) {
186                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
187                     } else {
188                         logger.debug("java.nio.Buffer.address: unavailable: {}",
189                                 ((Throwable) maybeAddressField).getMessage());
190                     }
191 
192                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
193                     // Let's just pretend unsafe is unavailable for overall simplicity.
194                     unsafe = null;
195                 }
196             }
197 
198             if (unsafe != null) {
199                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
200                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
201                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
202                 if (byteArrayIndexScale != 1) {
203                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
204                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
205                     unsafe = null;
206                 }
207             }
208         }
209         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
210         UNSAFE = unsafe;
211 
212         if (unsafe == null) {
213             ADDRESS_FIELD_OFFSET = -1;
214             BYTE_ARRAY_BASE_OFFSET = -1;
215             LONG_ARRAY_BASE_OFFSET = -1;
216             LONG_ARRAY_INDEX_SCALE = -1;
217             INT_ARRAY_BASE_OFFSET = -1;
218             INT_ARRAY_INDEX_SCALE = -1;
219             UNALIGNED = false;
220             DIRECT_BUFFER_CONSTRUCTOR_HANDLE = null;
221             ALLOCATE_ARRAY_HANDLE = null;
222         } else {
223             MethodHandle directBufferConstructorHandle;
224             long address = -1;
225             try {
226                 final Object maybeDirectBufferConstructorHandle =
227                         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
228                             try {
229                                 return Arrays
230                                         .stream(direct.getClass().getDeclaredConstructors())
231                                         .filter(declaredConstructor -> {
232                                             int parameterCount = declaredConstructor.getParameterCount();
233                                             if (parameterCount != 3 && parameterCount != 4) {
234                                                 return false;
235                                             }
236                                             Parameter[] parameters = declaredConstructor.getParameters();
237                                             return parameters[0].getType() == long.class
238                                                     && parameters[1].getType() == int.class
239                                                     && parameters[2].getType() == Object.class;
240                                         })
241                                         .findFirst()
242                                         .map(constructor -> {
243                                             try {
244                                                 Throwable cause = ReflectionUtil
245                                                         .trySetAccessible(constructor, true);
246                                                 if (cause != null) {
247                                                     return cause;
248                                                 }
249                                                 MethodHandle constructorHandle = MethodHandles.lookup()
250                                                         .unreflectConstructor(constructor);
251                                                 if (constructor.getParameterCount() == 4) {
252                                                     Object[] nullArgs = { null };
253                                                     constructorHandle = MethodHandles
254                                                             .insertArguments(constructorHandle, 3, nullArgs);
255                                                 }
256                                                 return constructorHandle;
257                                             } catch (SecurityException | IllegalAccessException e) {
258                                                 return e;
259                                             }
260                                         })
261                                         .orElseThrow();
262                             } catch (SecurityException | NoSuchElementException e) {
263                                 return e;
264                             }
265                         });
266 
267                 if (maybeDirectBufferConstructorHandle instanceof MethodHandle) {
268                     address = UNSAFE.allocateMemory(1);
269                     // try to use the constructor now
270                     try {
271                         directBufferConstructorHandle = (MethodHandle) maybeDirectBufferConstructorHandle;
272                         directBufferConstructorHandle.invoke(address, 1, (Object) null);
273                         logger.debug("direct buffer constructor: available");
274                     } catch (Throwable ignore) {
275                         directBufferConstructorHandle = null;
276                     }
277                 } else {
278                     if (logger.isTraceEnabled()) {
279                         logger.debug("direct buffer constructor: unavailable",
280                                 (Throwable) maybeDirectBufferConstructorHandle);
281                     } else {
282                         logger.debug("direct buffer constructor: unavailable: {}",
283                                 ((Throwable) maybeDirectBufferConstructorHandle).getMessage());
284                     }
285                     directBufferConstructorHandle = null;
286                 }
287             } finally {
288                 if (address != -1) {
289                     UNSAFE.freeMemory(address);
290                 }
291             }
292             DIRECT_BUFFER_CONSTRUCTOR_HANDLE = directBufferConstructorHandle;
293             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
294             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
295             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
296             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
297             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
298             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
299             final boolean unaligned;
300             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<>() {
301                 @Override
302                 public Object run() {
303                     try {
304                         Class<?> bitsClass =
305                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
306                         int version = javaVersion();
307                         if (unsafeStaticFieldOffsetSupported() && version >= 9) {
308                             // Java9/10 use all lowercase and later versions all uppercase.
309                             String fieldName = version >= 11 ? "UNALIGNED" : "unaligned";
310                             // On Java9 and later we try to directly access the field as we can do this without
311                             // adjust the accessible levels.
312                             try {
313                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
314                                 if (unalignedField.getType() == boolean.class) {
315                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
316                                     Object object = UNSAFE.staticFieldBase(unalignedField);
317                                     return UNSAFE.getBoolean(object, offset);
318                                 }
319                                 // There is something unexpected stored in the field,
320                                 // let us fall-back and try to use a reflective method call as last resort.
321                             } catch (NoSuchFieldException ignore) {
322                                 // We did not find the field we expected, move on.
323                             }
324                         }
325                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
326                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
327                         if (cause != null) {
328                             return cause;
329                         }
330                         return unalignedMethod.invoke(null);
331                     } catch (NoSuchMethodException | SecurityException
332                              | IllegalAccessException | ClassNotFoundException | InvocationTargetException e) {
333                         return e;
334                     }
335                 }
336             });
337 
338             if (maybeUnaligned instanceof Boolean) {
339                 unaligned = (Boolean) maybeUnaligned;
340                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
341             } else {
342                 String arch = SystemPropertyUtil.get("os.arch", "");
343                 //noinspection DynamicRegexReplaceableByCompiledPattern
344                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
345                 Throwable t = (Throwable) maybeUnaligned;
346                 if (logger.isTraceEnabled()) {
347                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
348                 } else {
349                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
350                 }
351             }
352 
353             UNALIGNED = unaligned;
354 
355             if (javaVersion() >= 9) {
356                 Object maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
357                     try {
358                         // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
359                         // sun.misc.Unsafe
360                         Class<?> internalUnsafeClass = getClassLoader(PlatformDependent0.class)
361                                 .loadClass("jdk.internal.misc.Unsafe");
362                         Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe");
363                         return method.invoke(null);
364                     } catch (Throwable e) {
365                         return e;
366                     }
367                 });
368                 if (!(maybeException instanceof Throwable)) {
369                     final Object finalInternalUnsafe = maybeException;
370                     maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
371                         try {
372                             return MethodHandles.lookup().findVirtual(finalInternalUnsafe.getClass(),
373                                     "allocateUninitializedArray",
374                                     MethodType.methodType(byte[].class, Class.class, int.class))
375                                     .bindTo(finalInternalUnsafe);
376                         } catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
377                             return e;
378                         }
379                     });
380 
381                     if (maybeException instanceof MethodHandle) {
382                         try {
383                             MethodHandle m = (MethodHandle) maybeException;
384                             byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
385                             assert bytes.length == 8;
386                             allocateArrayHandle = m;
387                         } catch (Throwable e) {
388                             maybeException = e;
389                         }
390                     }
391                 }
392 
393                 if (maybeException instanceof Throwable) {
394                     if (logger.isTraceEnabled()) {
395                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
396                                 (Throwable) maybeException);
397                     } else {
398                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
399                                 ((Throwable) maybeException).getMessage());
400                     }
401                 } else {
402                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
403                 }
404             } else {
405                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
406             }
407             ALLOCATE_ARRAY_HANDLE = allocateArrayHandle;
408         }
409 
410         if (javaVersion() > 9) {
411             ALIGN_SLICE = (Method) AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
412                 try {
413                     return ByteBuffer.class.getDeclaredMethod("alignedSlice", int.class);
414                 } catch (Exception e) {
415                     return null;
416                 }
417             });
418         } else {
419             ALIGN_SLICE = null;
420         }
421 
422         logger.debug("java.nio.DirectByteBuffer.<init>(long, int, Object): {}",
423                 DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null ? "available" : "unavailable");
424     }
425 
426     private static boolean unsafeStaticFieldOffsetSupported() {
427         return !RUNNING_IN_NATIVE_IMAGE;
428     }
429 
430     static boolean isExplicitNoUnsafe() {
431         return EXPLICIT_NO_UNSAFE_CAUSE != null;
432     }
433 
434     private static Throwable explicitNoUnsafeCause0() {
435         final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty5.noUnsafe", false);
436         logger.debug("-Dio.netty5.noUnsafe: {}", noUnsafe);
437 
438         if (noUnsafe) {
439             logger.debug("sun.misc.Unsafe: unavailable (io.netty5.noUnsafe)");
440             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty5.noUnsafe)");
441         }
442 
443         boolean tryUnsafe = SystemPropertyUtil.getBoolean("io.netty5.tryUnsafe", true);
444 
445         if (!tryUnsafe) {
446             String msg = "sun.misc.Unsafe: unavailable (io.netty5.tryUnsafe)";
447             logger.debug(msg);
448             return new UnsupportedOperationException(msg);
449         }
450 
451         return null;
452     }
453 
454     static boolean isUnaligned() {
455         return UNALIGNED;
456     }
457 
458     static boolean hasUnsafe() {
459         return UNSAFE != null;
460     }
461 
462     static Throwable getUnsafeUnavailabilityCause() {
463         return UNSAFE_UNAVAILABILITY_CAUSE;
464     }
465 
466     static boolean unalignedAccess() {
467         return UNALIGNED;
468     }
469 
470     static boolean hasDirectBufferNoCleanerConstructor() {
471         return DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null;
472     }
473 
474     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
475         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity, null);
476     }
477 
478     static ByteBuffer allocateDirectNoCleaner(int capacity) {
479         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
480         // Just use 1 to make it safe to use in all cases:
481         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
482         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity, null);
483     }
484 
485     static boolean hasAlignSliceMethod() {
486         return ALIGN_SLICE != null;
487     }
488 
489     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
490         try {
491             return (ByteBuffer) ALIGN_SLICE.invoke(buffer, alignment);
492         } catch (IllegalAccessException | InvocationTargetException e) {
493             throw new Error(e);
494         }
495     }
496 
497     static boolean hasAllocateArrayMethod() {
498         return ALLOCATE_ARRAY_HANDLE != null;
499     }
500 
501     static byte[] allocateUninitializedArray(int size) {
502         try {
503             return (byte[]) ALLOCATE_ARRAY_HANDLE.invoke(byte.class, size);
504         } catch (Throwable e) {
505             if (e instanceof Error) {
506                 throw (Error) e;
507             }
508             throw new Error(e);
509         }
510     }
511 
512     static ByteBuffer newDirectBuffer(long address, int capacity, Object attachment) {
513         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
514 
515         try {
516             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR_HANDLE.invoke(address, capacity, attachment);
517         } catch (Throwable cause) {
518             // Not expected to ever throw!
519             if (cause instanceof Error) {
520                 throw (Error) cause;
521             }
522             throw new Error(cause);
523         }
524     }
525 
526     static long directBufferAddress(ByteBuffer buffer) {
527         return getLong(buffer, ADDRESS_FIELD_OFFSET);
528     }
529 
530     static long byteArrayBaseOffset() {
531         return BYTE_ARRAY_BASE_OFFSET;
532     }
533 
534     static Object getObject(Object object, long fieldOffset) {
535         return UNSAFE.getObject(object, fieldOffset);
536     }
537 
538     static byte getByte(Object object, long fieldOffset) {
539         return UNSAFE.getByte(object, fieldOffset);
540     }
541 
542     static short getShort(Object object, long fieldOffset) {
543         return UNSAFE.getShort(object, fieldOffset);
544     }
545 
546     static char getChar(Object object, long fieldOffset) {
547         return UNSAFE.getChar(object, fieldOffset);
548     }
549 
550     static int getInt(Object object, long fieldOffset) {
551         return UNSAFE.getInt(object, fieldOffset);
552     }
553 
554     static float getFloat(Object object, long fieldOffset) {
555         return UNSAFE.getFloat(object, fieldOffset);
556     }
557 
558     static long getLong(Object object, long fieldOffset) {
559         return UNSAFE.getLong(object, fieldOffset);
560     }
561 
562     static double getDouble(Object object, long fieldOffset) {
563         return UNSAFE.getDouble(object, fieldOffset);
564     }
565 
566     static long objectFieldOffset(Field field) {
567         return UNSAFE.objectFieldOffset(field);
568     }
569 
570     static byte getByte(long address) {
571         return UNSAFE.getByte(address);
572     }
573 
574     static short getShort(long address) {
575         return UNSAFE.getShort(address);
576     }
577 
578     static int getInt(long address) {
579         return UNSAFE.getInt(address);
580     }
581 
582     static long getLong(long address) {
583         return UNSAFE.getLong(address);
584     }
585 
586     static byte getByte(byte[] data, int index) {
587         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
588     }
589 
590     static byte getByte(byte[] data, long index) {
591         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
592     }
593 
594     static short getShort(byte[] data, int index) {
595         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
596     }
597 
598     static int getInt(byte[] data, int index) {
599         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
600     }
601 
602     static int getInt(int[] data, long index) {
603         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
604     }
605 
606     static int getIntVolatile(long address) {
607         return UNSAFE.getIntVolatile(null, address);
608     }
609 
610     static void putIntOrdered(long adddress, int newValue) {
611         UNSAFE.putOrderedInt(null, adddress, newValue);
612     }
613 
614     static long getLong(byte[] data, int index) {
615         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
616     }
617 
618     static long getLong(long[] data, long index) {
619         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
620     }
621 
622     static void putByte(long address, byte value) {
623         UNSAFE.putByte(address, value);
624     }
625 
626     static void putShort(long address, short value) {
627         UNSAFE.putShort(address, value);
628     }
629 
630     static void putInt(long address, int value) {
631         UNSAFE.putInt(address, value);
632     }
633 
634     static void putLong(long address, long value) {
635         UNSAFE.putLong(address, value);
636     }
637 
638     static void putByte(byte[] data, int index, byte value) {
639         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
640     }
641 
642     static void putByte(Object data, long offset, byte value) {
643         UNSAFE.putByte(data, offset, value);
644     }
645 
646     static void putShort(byte[] data, int index, short value) {
647         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
648     }
649 
650     static void putShort(Object data, long offset, short value) {
651         UNSAFE.putShort(data, offset, value);
652     }
653 
654     static void putChar(Object data, long offset, char value) {
655         UNSAFE.putChar(data, offset, value);
656     }
657 
658     static void putInt(byte[] data, int index, int value) {
659         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
660     }
661 
662     static void putInt(Object data, long offset, int value) {
663         UNSAFE.putInt(data, offset, value);
664     }
665 
666     static void putFloat(Object data, long offset, float value) {
667         UNSAFE.putFloat(data, offset, value);
668     }
669 
670     static void putLong(byte[] data, int index, long value) {
671         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
672     }
673 
674     static void putLong(Object data, long offset, long value) {
675         UNSAFE.putLong(data, offset, value);
676     }
677 
678     static void putDouble(Object data, long offset, double value) {
679         UNSAFE.putDouble(data, offset, value);
680     }
681 
682     static void putObject(Object o, long offset, Object x) {
683         UNSAFE.putObject(o, offset, x);
684     }
685 
686     static void copyMemory(long srcAddr, long dstAddr, long length) {
687         // Manual safe-point polling is only needed prior Java9:
688         // See https://bugs.openjdk.java.net/browse/JDK-8149596
689         if (javaVersion() <= 8) {
690             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
691         } else {
692             UNSAFE.copyMemory(srcAddr, dstAddr, length);
693         }
694     }
695 
696     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
697         while (length > 0) {
698             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
699             UNSAFE.copyMemory(srcAddr, dstAddr, size);
700             length -= size;
701             srcAddr += size;
702             dstAddr += size;
703         }
704     }
705 
706     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
707         // Manual safe-point polling is only needed prior Java9:
708         // See https://bugs.openjdk.java.net/browse/JDK-8149596
709         if (javaVersion() <= 8) {
710             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
711         } else {
712             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
713         }
714     }
715 
716     private static void copyMemoryWithSafePointPolling(
717             Object src, long srcOffset, Object dst, long dstOffset, long length) {
718         while (length > 0) {
719             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
720             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
721             length -= size;
722             srcOffset += size;
723             dstOffset += size;
724         }
725     }
726 
727     static void setMemory(long address, long bytes, byte value) {
728         UNSAFE.setMemory(address, bytes, value);
729     }
730 
731     static void setMemory(Object o, long offset, long bytes, byte value) {
732         UNSAFE.setMemory(o, offset, bytes, value);
733     }
734 
735     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
736         int remainingBytes = length & 7;
737         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
738         final long diff = startPos2 - startPos1;
739         if (length >= 8) {
740             final long end = baseOffset1 + remainingBytes;
741             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
742                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
743                     return false;
744                 }
745             }
746         }
747         if (remainingBytes >= 4) {
748             remainingBytes -= 4;
749             long pos = baseOffset1 + remainingBytes;
750             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
751                 return false;
752             }
753         }
754         final long baseOffset2 = baseOffset1 + diff;
755         if (remainingBytes >= 2) {
756             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
757                     (remainingBytes == 2 ||
758                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
759         }
760         return remainingBytes == 0 ||
761                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
762     }
763 
764     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
765         long result = 0;
766         long remainingBytes = length & 7;
767         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
768         final long end = baseOffset1 + remainingBytes;
769         final long diff = startPos2 - startPos1;
770         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
771             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
772         }
773         if (remainingBytes >= 4) {
774             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
775             remainingBytes -= 4;
776         }
777         if (remainingBytes >= 2) {
778             long pos = end - remainingBytes;
779             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
780             remainingBytes -= 2;
781         }
782         if (remainingBytes == 1) {
783             long pos = end - 1;
784             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
785         }
786         return ConstantTimeUtils.equalsConstantTime(result, 0);
787     }
788 
789     static boolean isZero(byte[] bytes, int startPos, int length) {
790         if (length <= 0) {
791             return true;
792         }
793         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
794         int remainingBytes = length & 7;
795         final long end = baseOffset + remainingBytes;
796         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
797             if (UNSAFE.getLong(bytes, i) != 0) {
798                 return false;
799             }
800         }
801 
802         if (remainingBytes >= 4) {
803             remainingBytes -= 4;
804             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
805                 return false;
806             }
807         }
808         if (remainingBytes >= 2) {
809             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
810                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
811         }
812         return bytes[startPos] == 0;
813     }
814 
815     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
816         int hash = HASH_CODE_ASCII_SEED;
817         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
818         final int remainingBytes = length & 7;
819         final long end = baseOffset + remainingBytes;
820         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
821             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
822         }
823         if (remainingBytes == 0) {
824             return hash;
825         }
826         int hcConst = HASH_CODE_C1;
827         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
828             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
829             hcConst = HASH_CODE_C2;
830             baseOffset++;
831         }
832         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
833             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
834             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
835             baseOffset += 2;
836         }
837         if (remainingBytes >= 4) { // 4, 5, 6, 7
838             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
839         }
840         return hash;
841     }
842 
843     static int hashCodeAsciiCompute(long value, int hash) {
844         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
845         // code the same regardless of character case (upper case or lower case hash is the same).
846         return hash * HASH_CODE_C1 +
847                 // Low order int
848                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
849                 // High order int
850                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
851     }
852 
853     static int hashCodeAsciiSanitize(int value) {
854         return value & 0x1f1f1f1f;
855     }
856 
857     static int hashCodeAsciiSanitize(short value) {
858         return value & 0x1f1f;
859     }
860 
861     static int hashCodeAsciiSanitize(byte value) {
862         return value & 0x1f;
863     }
864 
865     static ClassLoader getClassLoader(final Class<?> clazz) {
866         if (System.getSecurityManager() == null) {
867             return clazz.getClassLoader();
868         } else {
869             return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) clazz::getClassLoader);
870         }
871     }
872 
873     static ClassLoader getContextClassLoader() {
874         if (System.getSecurityManager() == null) {
875             return Thread.currentThread().getContextClassLoader();
876         } else {
877             return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () ->
878                     Thread.currentThread().getContextClassLoader());
879         }
880     }
881 
882     static ClassLoader getSystemClassLoader() {
883         if (System.getSecurityManager() == null) {
884             return ClassLoader.getSystemClassLoader();
885         } else {
886             return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) ClassLoader::getSystemClassLoader);
887         }
888     }
889 
890     static int addressSize() {
891         return UNSAFE.addressSize();
892     }
893 
894     static long allocateMemory(long size) {
895         return UNSAFE.allocateMemory(size);
896     }
897 
898     static void freeMemory(long address) {
899         UNSAFE.freeMemory(address);
900     }
901 
902     static long reallocateMemory(long address, long newSize) {
903         return UNSAFE.reallocateMemory(address, newSize);
904     }
905 
906     static boolean isAndroid() {
907         return IS_ANDROID;
908     }
909 
910     private static boolean isAndroid0() {
911         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
912         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
913 
914         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
915         // OpenJDK is used, which means `Unsafe` will actually work as expected.
916 
917         // Android sets this property to Dalvik, regardless of whether it actually is.
918         String vmName = SystemPropertyUtil.get("java.vm.name");
919         boolean isAndroid = "Dalvik".equals(vmName);
920         if (isAndroid) {
921             logger.debug("Platform: Android");
922         }
923         return isAndroid;
924     }
925 
926     private static boolean explicitTryReflectionSetAccessible0() {
927         // we disable reflective access
928         return SystemPropertyUtil.getBoolean("io.netty5.tryReflectionSetAccessible", javaVersion() < 9);
929     }
930 
931     static boolean isExplicitTryReflectionSetAccessible() {
932         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
933     }
934 
935     static int javaVersion() {
936         return JAVA_VERSION;
937     }
938 
939     private static int javaVersion0() {
940         final int majorVersion;
941 
942         if (isAndroid0()) {
943             majorVersion = 6;
944         } else {
945             majorVersion = majorVersionFromJavaSpecificationVersion();
946         }
947 
948         logger.debug("Java version: {}", majorVersion);
949 
950         return majorVersion;
951     }
952 
953     // Package-private for testing only
954     static int majorVersionFromJavaSpecificationVersion() {
955         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
956     }
957 
958     // Package-private for testing only
959     static int majorVersion(final String javaSpecVersion) {
960         final String[] components = javaSpecVersion.split("\\.");
961         final int[] version = new int[components.length];
962         for (int i = 0; i < components.length; i++) {
963             version[i] = Integer.parseInt(components[i]);
964         }
965 
966         if (version[0] == 1) {
967             assert version[1] >= 6;
968             return version[1];
969         } else {
970             return version[0];
971         }
972     }
973 
974     private PlatformDependent0() {
975     }
976 }