diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/util/Fingerprint.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/util/Fingerprint.java | 80 |
1 files changed, 28 insertions, 52 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/util/Fingerprint.java b/src/main/java/com/google/devtools/build/lib/util/Fingerprint.java index a2bafa6f6a..556bafdb0a 100644 --- a/src/main/java/com/google/devtools/build/lib/util/Fingerprint.java +++ b/src/main/java/com/google/devtools/build/lib/util/Fingerprint.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.util; import com.google.common.io.ByteStreams; +import com.google.devtools.build.lib.vfs.DigestHashFunction; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.protobuf.CodedOutputStream; @@ -23,47 +24,45 @@ import java.nio.charset.StandardCharsets; import java.security.DigestException; import java.security.DigestOutputStream; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import javax.annotation.Nullable; /** - * Simplified wrapper for MD5 message digests. + * Simplified wrapper for computing message digests. * * @see java.security.MessageDigest */ public final class Fingerprint implements Consumer<String> { - private static final MessageDigest MD5_PROTOTYPE; - private static final boolean MD5_PROTOTYPE_SUPPORTS_CLONE; - - static { - MD5_PROTOTYPE = getMd5Instance(); - MD5_PROTOTYPE_SUPPORTS_CLONE = supportsClone(MD5_PROTOTYPE); - } - // Make novel use of a CodedOutputStream, which is good at efficiently serializing data. By // flushing at the end of each digest we can continue to use the stream. private final CodedOutputStream codedOut; - private final MessageDigest md5; + private final MessageDigest messageDigest; /** Creates and initializes a new instance. */ - public Fingerprint() { - md5 = cloneOrCreateMd5(); + public Fingerprint(DigestHashFunction digestFunction) { + messageDigest = digestFunction.cloneOrCreateMessageDigest(); // This is a lot of indirection, but CodedOutputStream does a reasonable job of converting // strings to bytes without creating a whole bunch of garbage, which pays off. - codedOut = CodedOutputStream.newInstance( - new DigestOutputStream(ByteStreams.nullOutputStream(), md5), - /*bufferSize=*/ 1024); + codedOut = + CodedOutputStream.newInstance( + new DigestOutputStream(ByteStreams.nullOutputStream(), messageDigest), + /*bufferSize=*/ 1024); + } + + public Fingerprint() { + // TODO(b/112460990): Use the value from DigestHashFunction.getDefault(), but check for + // contention. + this(DigestHashFunction.MD5); } /** * Completes the hash computation by doing final operations and resets the underlying state, * allowing this instance to be used again. * - * @return the MD5 digest as a 16-byte array + * @return the digest as a 16-byte array * @see java.security.MessageDigest#digest() */ public byte[] digestAndReset() { @@ -72,7 +71,7 @@ public final class Fingerprint implements Consumer<String> { } catch (IOException e) { throw new IllegalStateException("failed to flush", e); } - return md5.digest(); + return messageDigest.digest(); } /** @@ -87,7 +86,7 @@ public final class Fingerprint implements Consumer<String> { public void digestAndReset(byte[] buf, int offset, int len) { try { codedOut.flush(); - md5.digest(buf, offset, len); + messageDigest.digest(buf, offset, len); } catch (IOException e) { throw new IllegalStateException("failed to flush", e); } catch (DigestException e) { @@ -250,18 +249,6 @@ public final class Fingerprint implements Consumer<String> { return this; } - private static MessageDigest cloneOrCreateMd5() { - if (MD5_PROTOTYPE_SUPPORTS_CLONE) { - try { - return (MessageDigest) MD5_PROTOTYPE.clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException("Could not clone md5", e); - } - } else { - return getMd5Instance(); - } - } - private static String hexDigest(byte[] digest) { StringBuilder b = new StringBuilder(32); for (int i = 0; i < digest.length; i++) { @@ -272,33 +259,22 @@ public final class Fingerprint implements Consumer<String> { return b.toString(); } - private static MessageDigest getMd5Instance() { - try { - return MessageDigest.getInstance("md5"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("md5 not available", e); - } - } - - private static boolean supportsClone(MessageDigest toCheck) { - try { - toCheck.clone(); - return true; - } catch (CloneNotSupportedException e) { - return false; - } - } - // -------- Convenience methods ---------------------------- /** - * Computes the hex digest from a String using UTF8 encoding and returning - * the hexDigest(). + * Computes the hex digest from a String using UTF8 encoding and returning the hexDigest(). * * @param input the String from which to compute the digest */ - public static String md5Digest(String input) { - return hexDigest(cloneOrCreateMd5().digest(input.getBytes(StandardCharsets.UTF_8))); + public static String getHexDigest(String input) { + // TODO(b/112460990): This convenience method, if kept should not use MD5 by default, but should + // use the value from DigestHashFunction.getDefault(). However, this gets called during class + // loading in a few places, before setDefault() has been called, so these call-sites should be + // removed before this can be done safely. + return hexDigest( + DigestHashFunction.MD5 + .cloneOrCreateMessageDigest() + .digest(input.getBytes(StandardCharsets.UTF_8))); } @Override |