From afb35a5d1be4a9718376098ef991496d5548dc4d Mon Sep 17 00:00:00 2001 From: lowasser Date: Mon, 12 Feb 2024 13:48:46 -0800 Subject: [PATCH] Optimize checksum-based hash functions on non-array ByteBuffers by at least an order of magnitude, at least for Java 9+. RELNOTES=`hash`: Optimized `Checksum`-based hash functions for Java 9+. PiperOrigin-RevId: 606354141 --- .../common/hash/ChecksumHashFunction.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/guava/src/com/google/common/hash/ChecksumHashFunction.java b/guava/src/com/google/common/hash/ChecksumHashFunction.java index 159adbb8194b..454f3098e3ee 100644 --- a/guava/src/com/google/common/hash/ChecksumHashFunction.java +++ b/guava/src/com/google/common/hash/ChecksumHashFunction.java @@ -18,8 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.errorprone.annotations.Immutable; +import com.google.j2objc.annotations.J2ObjCIncompatible; import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.nio.ByteBuffer; import java.util.zip.Checksum; +import org.checkerframework.checker.nullness.qual.Nullable; /** * {@link HashFunction} adapter for {@link Checksum} instances. @@ -74,6 +80,14 @@ protected void update(byte[] bytes, int off, int len) { checksum.update(bytes, off, len); } + @Override + @J2ObjCIncompatible + protected void update(ByteBuffer b) { + if (!ChecksumMethodHandles.updateByteBuffer(checksum, b)) { + super.update(b); + } + } + @Override public HashCode hash() { long value = checksum.getValue(); @@ -90,5 +104,47 @@ public HashCode hash() { } } + @J2ObjCIncompatible + @SuppressWarnings("unused") + private static final class ChecksumMethodHandles { + private static final @Nullable MethodHandle UPDATE_BB = updateByteBuffer(); + + @IgnoreJRERequirement // https://github.com/mojohaus/animal-sniffer/issues/67 + static boolean updateByteBuffer(Checksum cs, ByteBuffer bb) { + if (UPDATE_BB != null) { + try { + UPDATE_BB.invokeExact(cs, bb); + } catch (Error t) { + throw t; + } catch (Throwable t) { + throw new AssertionError(t); + } + return true; + } else { + return false; + } + } + + private static @Nullable MethodHandle updateByteBuffer() { + try { + Class clazz = Class.forName("java.util.zip.Checksum"); + return MethodHandles.lookup() + .findVirtual(clazz, "update", MethodType.methodType(void.class, ByteBuffer.class)); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + // That API is public. + throw newLinkageError(e); + } catch (NoSuchMethodException e) { + // Only introduced in Java 9. + return null; + } + } + + private static LinkageError newLinkageError(Throwable cause) { + return new LinkageError(cause.toString(), cause); + } + } + private static final long serialVersionUID = 0L; }