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