1 /* 2 * Copyright (C) 2015 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.hash; 16 17 import com.google.common.primitives.Longs; 18 import java.lang.reflect.Field; 19 import java.nio.ByteOrder; 20 import java.security.AccessController; 21 import java.security.PrivilegedActionException; 22 import java.security.PrivilegedExceptionAction; 23 import sun.misc.Unsafe; 24 25 /** 26 * Utility functions for loading and storing values from a byte array. 27 * 28 * @author Kevin Damm 29 * @author Kyle Maddison 30 */ 31 @ElementTypesAreNonnullByDefault 32 final class LittleEndianByteArray { 33 34 /** The instance that actually does the work; delegates to Unsafe or a pure-Java fallback. */ 35 private static final LittleEndianBytes byteArray; 36 37 /** 38 * Load 8 bytes into long in a little endian manner, from the substring between position and 39 * position + 8. The array must have at least 8 bytes from offset (inclusive). 40 * 41 * @param input the input bytes 42 * @param offset the offset into the array at which to start 43 * @return a long of a concatenated 8 bytes 44 */ load64(byte[] input, int offset)45 static long load64(byte[] input, int offset) { 46 // We don't want this in production code as this is the most critical part of the loop. 47 assert input.length >= offset + 8; 48 // Delegates to the fast (unsafe) version or the fallback. 49 return byteArray.getLongLittleEndian(input, offset); 50 } 51 52 /** 53 * Similar to load64, but allows offset + 8 > input.length, padding the result with zeroes. This 54 * has to explicitly reverse the order of the bytes as it packs them into the result which makes 55 * it slower than the native version. 56 * 57 * @param input the input bytes 58 * @param offset the offset into the array at which to start reading 59 * @param length the number of bytes from the input to read 60 * @return a long of a concatenated 8 bytes 61 */ load64Safely(byte[] input, int offset, int length)62 static long load64Safely(byte[] input, int offset, int length) { 63 long result = 0; 64 // Due to the way we shift, we can stop iterating once we've run out of data, the rest 65 // of the result already being filled with zeros. 66 67 // This loop is critical to performance, so please check HashBenchmark if altering it. 68 int limit = Math.min(length, 8); 69 for (int i = 0; i < limit; i++) { 70 // Shift value left while iterating logically through the array. 71 result |= (input[offset + i] & 0xFFL) << (i * 8); 72 } 73 return result; 74 } 75 76 /** 77 * Store 8 bytes into the provided array at the indicated offset, using the value provided. 78 * 79 * @param sink the output byte array 80 * @param offset the offset into the array at which to start writing 81 * @param value the value to write 82 */ store64(byte[] sink, int offset, long value)83 static void store64(byte[] sink, int offset, long value) { 84 // We don't want to assert in production code. 85 assert offset >= 0 && offset + 8 <= sink.length; 86 // Delegates to the fast (unsafe)version or the fallback. 87 byteArray.putLongLittleEndian(sink, offset, value); 88 } 89 90 /** 91 * Load 4 bytes from the provided array at the indicated offset. 92 * 93 * @param source the input bytes 94 * @param offset the offset into the array at which to start 95 * @return the value found in the array in the form of a long 96 */ load32(byte[] source, int offset)97 static int load32(byte[] source, int offset) { 98 // TODO(user): Measure the benefit of delegating this to LittleEndianBytes also. 99 return (source[offset] & 0xFF) 100 | ((source[offset + 1] & 0xFF) << 8) 101 | ((source[offset + 2] & 0xFF) << 16) 102 | ((source[offset + 3] & 0xFF) << 24); 103 } 104 105 /** 106 * Indicates that the loading of Unsafe was successful and the load and store operations will be 107 * very efficient. May be useful for calling code to fall back on an alternative implementation 108 * that is slower than Unsafe.get/store but faster than the pure-Java mask-and-shift. 109 */ usingUnsafe()110 static boolean usingUnsafe() { 111 return (byteArray instanceof UnsafeByteArray); 112 } 113 114 /** 115 * Common interface for retrieving a 64-bit long from a little-endian byte array. 116 * 117 * <p>This abstraction allows us to use single-instruction load and put when available, or fall 118 * back on the slower approach of using Longs.fromBytes(byte...). 119 */ 120 private interface LittleEndianBytes { getLongLittleEndian(byte[] array, int offset)121 long getLongLittleEndian(byte[] array, int offset); 122 putLongLittleEndian(byte[] array, int offset, long value)123 void putLongLittleEndian(byte[] array, int offset, long value); 124 } 125 126 /** 127 * The only reference to Unsafe is in this nested class. We set things up so that if 128 * Unsafe.theUnsafe is inaccessible, the attempt to load the nested class fails, and the outer 129 * class's static initializer can fall back on a non-Unsafe version. 130 */ 131 private enum UnsafeByteArray implements LittleEndianBytes { 132 // Do *not* change the order of these constants! 133 UNSAFE_LITTLE_ENDIAN { 134 @Override getLongLittleEndian(byte[] array, int offset)135 public long getLongLittleEndian(byte[] array, int offset) { 136 return theUnsafe.getLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET); 137 } 138 139 @Override putLongLittleEndian(byte[] array, int offset, long value)140 public void putLongLittleEndian(byte[] array, int offset, long value) { 141 theUnsafe.putLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET, value); 142 } 143 }, 144 UNSAFE_BIG_ENDIAN { 145 @Override getLongLittleEndian(byte[] array, int offset)146 public long getLongLittleEndian(byte[] array, int offset) { 147 long bigEndian = theUnsafe.getLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET); 148 // The hardware is big-endian, so we need to reverse the order of the bytes. 149 return Long.reverseBytes(bigEndian); 150 } 151 152 @Override putLongLittleEndian(byte[] array, int offset, long value)153 public void putLongLittleEndian(byte[] array, int offset, long value) { 154 // Reverse the order of the bytes before storing, since we're on big-endian hardware. 155 long littleEndianValue = Long.reverseBytes(value); 156 theUnsafe.putLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET, littleEndianValue); 157 } 158 }; 159 160 // Provides load and store operations that use native instructions to get better performance. 161 private static final Unsafe theUnsafe; 162 163 // The offset to the first element in a byte array. 164 private static final int BYTE_ARRAY_BASE_OFFSET; 165 166 /** 167 * Returns an Unsafe. Suitable for use in a 3rd party package. Replace with a simple call to 168 * Unsafe.getUnsafe when integrating into a JDK. 169 * 170 * @return an Unsafe instance if successful 171 */ getUnsafe()172 private static Unsafe getUnsafe() { 173 try { 174 return Unsafe.getUnsafe(); 175 } catch (SecurityException tryReflectionInstead) { 176 // We'll try reflection instead. 177 } 178 try { 179 return AccessController.doPrivileged( 180 (PrivilegedExceptionAction<Unsafe>) 181 () -> { 182 Class<Unsafe> k = Unsafe.class; 183 for (Field f : k.getDeclaredFields()) { 184 f.setAccessible(true); 185 Object x = f.get(null); 186 if (k.isInstance(x)) { 187 return k.cast(x); 188 } 189 } 190 throw new NoSuchFieldError("the Unsafe"); 191 }); 192 } catch (PrivilegedActionException e) { 193 throw new RuntimeException("Could not initialize intrinsics", e.getCause()); 194 } 195 } 196 197 static { 198 theUnsafe = getUnsafe(); 199 BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); 200 201 // sanity check - this should never fail 202 if (theUnsafe.arrayIndexScale(byte[].class) != 1) { AssertionError()203 throw new AssertionError(); 204 } 205 } 206 } 207 208 /** Fallback implementation for when Unsafe is not available in our current environment. */ 209 private enum JavaLittleEndianBytes implements LittleEndianBytes { 210 INSTANCE { 211 @Override getLongLittleEndian(byte[] source, int offset)212 public long getLongLittleEndian(byte[] source, int offset) { 213 return Longs.fromBytes( 214 source[offset + 7], 215 source[offset + 6], 216 source[offset + 5], 217 source[offset + 4], 218 source[offset + 3], 219 source[offset + 2], 220 source[offset + 1], 221 source[offset]); 222 } 223 224 @Override putLongLittleEndian(byte[] sink, int offset, long value)225 public void putLongLittleEndian(byte[] sink, int offset, long value) { 226 long mask = 0xFFL; 227 for (int i = 0; i < 8; mask <<= 8, i++) { 228 sink[offset + i] = (byte) ((value & mask) >> (i * 8)); 229 } 230 } 231 } 232 } 233 234 static { 235 LittleEndianBytes theGetter = JavaLittleEndianBytes.INSTANCE; 236 try { 237 /* 238 * UnsafeByteArray uses Unsafe.getLong() in an unsupported way, which is known to cause 239 * crashes on Android when running in 32-bit mode. For maximum safety, we shouldn't use 240 * Unsafe.getLong() at all, but the performance benefit on x86_64 is too great to ignore, so 241 * as a compromise, we enable the optimization only on platforms that we specifically know to 242 * work. 243 * 244 * In the future, the use of Unsafe.getLong() should be replaced by ByteBuffer.getLong(), 245 * which will have an efficient native implementation in JDK 9. 246 * 247 */ 248 String arch = System.getProperty("os.arch"); 249 if ("amd64".equals(arch)) { 250 theGetter = 251 ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN) 252 ? UnsafeByteArray.UNSAFE_LITTLE_ENDIAN 253 : UnsafeByteArray.UNSAFE_BIG_ENDIAN; 254 } 255 } catch (Throwable t) { 256 // ensure we really catch *everything* 257 } 258 byteArray = theGetter; 259 } 260 261 /** Deter instantiation of this class. */ LittleEndianByteArray()262 private LittleEndianByteArray() {} 263 } 264