aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/vfs/DigestHashFunction.java
diff options
context:
space:
mode:
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.java99
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();
+ }
}