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