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