diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java | 99 |
1 files changed, 80 insertions, 19 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java b/src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java index 12aee0757b..123e4b5a9c 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java @@ -14,16 +14,26 @@ package com.google.devtools.build.lib.vfs; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.OptionsParsingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; -/** Type of hash function to use for digesting files. */ +/** + * Type of hash function to use for digesting files. + * + * <p>This tracks parallel {@link java.security.MessageDigest} and {@link HashFunction} interfaces + * for each provided hash, as Bazel uses both - MessageDigest where performance is critical and + * HashFunctions where ease-of-use wins over. + */ // The underlying HashFunctions are immutable and thread safe. public class DigestHashFunction { // This map must be declared first to make sure that calls to register() have it ready. @@ -36,33 +46,24 @@ public class DigestHashFunction { private static DigestHashFunction defaultHash; private static boolean defaultHasBeenSet = false; - private final HashFunction hash; + private final HashFunction hashFunction; private final String name; + private final MessageDigest messageDigestPrototype; + private final boolean messageDigestPrototypeSupportsClone; - private DigestHashFunction(HashFunction hash, String name) { - this.hash = hash; + private DigestHashFunction(HashFunction hashFunction, String name) { + this.hashFunction = hashFunction; this.name = name; - } - - public HashFunction getHash() { - return hash; - } - - public boolean isValidDigest(byte[] digest) { - // TODO(b/109764197): Remove this check to accept variable-length hashes. - return digest != null && digest.length * 8 == hash.bits(); - } - - @Override - public String toString() { - return name; + this.messageDigestPrototype = getMessageDigestInstance(); + this.messageDigestPrototypeSupportsClone = supportsClone(messageDigestPrototype); } /** * Creates a new DigestHashFunction that is registered to be recognized by its name in {@link * DigestFunctionConverter}. * - * @param hashName the canonical name for this hash function. + * @param hashName the canonical name for this hash function - and the name that can be used to + * uncover the MessageDigest. * @param altNames alternative names that will be mapped to this function by the converter but * will not serve as the canonical name for the DigestHashFunction. * @param hash The {@link HashFunction} to register. @@ -70,6 +71,15 @@ public class DigestHashFunction { */ public static DigestHashFunction register( HashFunction hash, String hashName, String... altNames) { + try { + MessageDigest.getInstance(hashName); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException( + "The hash function name provided does not correspond to a valid MessageDigest: " + + hashName, + e); + } + DigestHashFunction hashFunction = new DigestHashFunction(hash, hashName); List<String> names = ImmutableList.<String>builder().add(hashName).add(altNames).build(); synchronized (hashFunctionRegistry) { @@ -149,4 +159,55 @@ public class DigestHashFunction { return "hash function"; } } + + public HashFunction getHashFunction() { + return hashFunction; + } + + public MessageDigest cloneOrCreateMessageDigest() { + if (messageDigestPrototypeSupportsClone) { + try { + return (MessageDigest) messageDigestPrototype.clone(); + } catch (CloneNotSupportedException e) { + // We checked at initialization that this could be cloned, so this should never happen. + throw new IllegalStateException("Could not clone message digest", e); + } + } else { + return getMessageDigestInstance(); + } + } + + public boolean isValidDigest(byte[] digest) { + // TODO(b/109764197): Remove this check to accept variable-length hashes. + return digest != null && digest.length * 8 == hashFunction.bits(); + } + + @Override + public String toString() { + return name; + } + + private MessageDigest getMessageDigestInstance() { + try { + return MessageDigest.getInstance(name); + } catch (NoSuchAlgorithmException e) { + // We check when we register() this digest function that the message digest exists. This + // should never happen. + throw new IllegalStateException("message digest " + name + " not available", e); + } + } + + private static boolean supportsClone(MessageDigest toCheck) { + try { + toCheck.clone(); + return true; + } catch (CloneNotSupportedException e) { + return false; + } + } + + @VisibleForTesting + static Collection<DigestHashFunction> getPossibleHashFunctions() { + return hashFunctionRegistry.values(); + } } |