15
15
package com .google .common .hash ;
16
16
17
17
import static java .lang .Math .min ;
18
+ import static java .lang .invoke .MethodHandles .byteArrayViewVarHandle ;
19
+ import static java .nio .ByteOrder .LITTLE_ENDIAN ;
18
20
21
+ import com .google .common .annotations .VisibleForTesting ;
19
22
import com .google .common .primitives .Longs ;
23
+ import com .google .j2objc .annotations .J2ObjCIncompatible ;
24
+ import java .lang .invoke .VarHandle ;
20
25
import java .lang .reflect .Field ;
21
26
import java .nio .ByteOrder ;
22
27
import java .security .AccessController ;
23
28
import java .security .PrivilegedActionException ;
24
29
import java .security .PrivilegedExceptionAction ;
30
+ import org .jspecify .annotations .Nullable ;
25
31
import sun .misc .Unsafe ;
26
32
27
33
/**
32
38
*/
33
39
final class LittleEndianByteArray {
34
40
35
- /** The instance that actually does the work; delegates to Unsafe or a pure-Java fallback. */
36
- private static final LittleEndianBytes byteArray ;
41
+ /**
42
+ * The instance that actually does the work; delegates to VarHandle, Unsafe, or a Java-8
43
+ * compatible pure-Java fallback.
44
+ */
45
+ private static final LittleEndianBytes byteArray = makeGetter ();
37
46
38
47
/**
39
48
* Load 8 bytes into long in a little endian manner, from the substring between position and
@@ -104,12 +113,12 @@ static int load32(byte[] source, int offset) {
104
113
}
105
114
106
115
/**
107
- * Indicates that the loading of Unsafe was successful and the load and store operations will be
108
- * very efficient . May be useful for calling code to fall back on an alternative implementation
109
- * that is slower than Unsafe.get/store but faster than the pure-Java mask-and-shift.
116
+ * Indicates that the load and store operations will be very efficient because of use of VarHandle
117
+ * or Unsafe . May be useful for calling code to fall back on an alternative implementation that is
118
+ * slower than those implementations but faster than the pure-Java mask-and-shift.
110
119
*/
111
- static boolean usingUnsafe () {
112
- return ( byteArray instanceof UnsafeByteArray );
120
+ static boolean usingFastPath () {
121
+ return byteArray . usesFastPath ( );
113
122
}
114
123
115
124
/**
@@ -122,6 +131,38 @@ private interface LittleEndianBytes {
122
131
long getLongLittleEndian (byte [] array , int offset );
123
132
124
133
void putLongLittleEndian (byte [] array , int offset , long value );
134
+
135
+ boolean usesFastPath ();
136
+ }
137
+
138
+ /** VarHandle-based implementation. */
139
+ @ J2ObjCIncompatible
140
+ // We use this class only after confirming that VarHandle is available at runtime.
141
+ @ SuppressWarnings ("Java8ApiChecker" )
142
+ @ IgnoreJRERequirement
143
+ private enum VarHandleLittleEndianBytes implements LittleEndianBytes {
144
+ INSTANCE {
145
+ @ Override
146
+ public long getLongLittleEndian (byte [] array , int offset ) {
147
+ return (long ) HANDLE .get (array , offset );
148
+ }
149
+
150
+ @ Override
151
+ public void putLongLittleEndian (byte [] array , int offset , long value ) {
152
+ HANDLE .set (array , offset , value );
153
+ }
154
+ };
155
+
156
+ @ Override
157
+ public boolean usesFastPath () {
158
+ return true ;
159
+ }
160
+
161
+ /*
162
+ * non-private so that our `-source 8` build doesn't need to generate a synthetic accessor
163
+ * method, whose mention of VarHandle would upset WriteReplaceOverridesTest under Java 8.
164
+ */
165
+ static final VarHandle HANDLE = byteArrayViewVarHandle (long [].class , LITTLE_ENDIAN );
125
166
}
126
167
127
168
/**
@@ -130,7 +171,8 @@ private interface LittleEndianBytes {
130
171
* class's static initializer can fall back on a non-Unsafe version.
131
172
*/
132
173
@ SuppressWarnings ({"SunApi" , "removal" }) // b/345822163
133
- private enum UnsafeByteArray implements LittleEndianBytes {
174
+ @ VisibleForTesting
175
+ enum UnsafeByteArray implements LittleEndianBytes {
134
176
// Do *not* change the order of these constants!
135
177
UNSAFE_LITTLE_ENDIAN {
136
178
@ Override
@@ -159,6 +201,11 @@ public void putLongLittleEndian(byte[] array, int offset, long value) {
159
201
}
160
202
};
161
203
204
+ @ Override
205
+ public boolean usesFastPath () {
206
+ return true ;
207
+ }
208
+
162
209
// Provides load and store operations that use native instructions to get better performance.
163
210
private static final Unsafe theUnsafe ;
164
211
@@ -207,7 +254,10 @@ private static Unsafe getUnsafe() {
207
254
}
208
255
}
209
256
210
- /** Fallback implementation for when Unsafe is not available in our current environment. */
257
+ /**
258
+ * Fallback implementation for when VarHandle and Unsafe are not available in our current
259
+ * environment.
260
+ */
211
261
private enum JavaLittleEndianBytes implements LittleEndianBytes {
212
262
INSTANCE {
213
263
@ Override
@@ -230,11 +280,21 @@ public void putLongLittleEndian(byte[] sink, int offset, long value) {
230
280
sink [offset + i ] = (byte ) ((value & mask ) >> (i * 8 ));
231
281
}
232
282
}
283
+
284
+ @ Override
285
+ public boolean usesFastPath () {
286
+ return false ;
287
+ }
233
288
}
234
289
}
235
290
236
- static {
237
- LittleEndianBytes theGetter = JavaLittleEndianBytes .INSTANCE ;
291
+ static LittleEndianBytes makeGetter () {
292
+ LittleEndianBytes usingVarHandle =
293
+ VarHandleLittleEndianBytesMaker .INSTANCE .tryMakeVarHandleLittleEndianBytes ();
294
+ if (usingVarHandle != null ) {
295
+ return usingVarHandle ;
296
+ }
297
+
238
298
try {
239
299
/*
240
300
* UnsafeByteArray uses Unsafe.getLong() in an unsupported way, which is known to cause
@@ -249,15 +309,40 @@ public void putLongLittleEndian(byte[] sink, int offset, long value) {
249
309
*/
250
310
String arch = System .getProperty ("os.arch" );
251
311
if ("amd64" .equals (arch ) || "aarch64" .equals (arch )) {
252
- theGetter =
253
- ByteOrder .nativeOrder ().equals (ByteOrder .LITTLE_ENDIAN )
254
- ? UnsafeByteArray .UNSAFE_LITTLE_ENDIAN
255
- : UnsafeByteArray .UNSAFE_BIG_ENDIAN ;
312
+ return ByteOrder .nativeOrder ().equals (ByteOrder .LITTLE_ENDIAN )
313
+ ? UnsafeByteArray .UNSAFE_LITTLE_ENDIAN
314
+ : UnsafeByteArray .UNSAFE_BIG_ENDIAN ;
256
315
}
257
316
} catch (Throwable t ) {
258
317
// ensure we really catch *everything*
259
318
}
260
- byteArray = theGetter ;
319
+
320
+ return JavaLittleEndianBytes .INSTANCE ;
321
+ }
322
+
323
+ // Compare AbstractFuture.VarHandleAtomicHelperMaker.
324
+ private enum VarHandleLittleEndianBytesMaker {
325
+ INSTANCE {
326
+ /**
327
+ * Implementation used by non-J2ObjC environments (aside, of course, from those that have
328
+ * supersource for the entirety of {@link AbstractFuture}).
329
+ */
330
+ @ Override
331
+ @ J2ObjCIncompatible
332
+ @ Nullable LittleEndianBytes tryMakeVarHandleLittleEndianBytes () {
333
+ try {
334
+ Class .forName ("java.lang.invoke.VarHandle" );
335
+ } catch (ClassNotFoundException beforeJava9 ) {
336
+ return null ;
337
+ }
338
+ return VarHandleLittleEndianBytes .INSTANCE ;
339
+ }
340
+ };
341
+
342
+ /** Implementation used by J2ObjC environments, overridden for other environments. */
343
+ @ Nullable LittleEndianBytes tryMakeVarHandleLittleEndianBytes () {
344
+ return null ;
345
+ }
261
346
}
262
347
263
348
/** Deter instantiation of this class. */
0 commit comments