aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/util/Fingerprint.java
diff options
context:
space:
mode:
authorGravatar Michajlo Matijkiw <michajlo@google.com>2016-11-30 18:59:59 +0000
committerGravatar Irina Iancu <elenairina@google.com>2016-12-01 10:15:39 +0000
commit43ad03290228adc8187853f83be073119c6f9aaa (patch)
tree12192e2ad39db9c10141e4f91f98fc43f832b814 /src/main/java/com/google/devtools/build/lib/util/Fingerprint.java
parent1f95a4e35b19bea98dbaca3130729c2465b85f95 (diff)
Use MessageDigest instead of HashFunction for Fingerprint
This allows us to reset and reuse the underlying digest implementation, which guava's HashFunction doesn't allow. We do take the clone-if-possible page out of guava's book. -- MOS_MIGRATED_REVID=140624836
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.java110
1 files changed, 81 insertions, 29 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 75737c34f8..1ca8ecf120 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
@@ -17,15 +17,15 @@ package com.google.devtools.build.lib.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.Iterables;
-import com.google.common.hash.HashFunction;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.UUID;
-
import javax.annotation.Nullable;
/**
@@ -35,14 +35,26 @@ import javax.annotation.Nullable;
*/
public final class Fingerprint {
- private Hasher hasher;
- private static final HashFunction MD5_HASH_FUNCTION = Hashing.md5();
+ private static final byte[] TRUE_BYTES = new byte[] { 1 };
+ private static final byte[] FALSE_BYTES = new byte[] { 0 };
+
+ 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);
+ }
+
+ private final ByteBuffer scratch;
+ private final MessageDigest md5;
/**
- * Creates and initializes a new Hasher.
+ * Creates and initializes a new instance.
*/
public Fingerprint() {
- reset();
+ scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
+ md5 = cloneOrCreateMd5();
}
/**
@@ -54,9 +66,8 @@ public final class Fingerprint {
* @see java.security.MessageDigest#digest()
*/
public byte[] digestAndReset() {
- byte[] bytes = hasher.hash().asBytes();
- reset();
- return bytes;
+ // Reset is implicit.
+ return md5.digest();
}
/**
@@ -67,9 +78,7 @@ public final class Fingerprint {
* @return the MD5 digest as a 32-character string of hexadecimal digits
*/
public String hexDigestAndReset() {
- String hexDigest = hasher.hash().toString();
- reset();
- return hexDigest;
+ return hexDigest(digestAndReset());
}
/**
@@ -79,7 +88,7 @@ public final class Fingerprint {
* @see java.security.MessageDigest#update(byte[])
*/
public Fingerprint addBytes(byte[] input) {
- hasher.putBytes(input);
+ md5.update(input);
return this;
}
@@ -92,7 +101,7 @@ public final class Fingerprint {
* @see java.security.MessageDigest#update(byte[], int, int)
*/
public Fingerprint addBytes(byte[] input, int offset, int len) {
- hasher.putBytes(input, offset, len);
+ md5.update(input, offset, len);
return this;
}
@@ -100,7 +109,7 @@ public final class Fingerprint {
* Updates the digest with a boolean value.
*/
public Fingerprint addBoolean(boolean input) {
- addBytes(new byte[] { (byte) (input ? 1 : 0) });
+ md5.update(input ? TRUE_BYTES : FALSE_BYTES);
return this;
}
@@ -108,8 +117,7 @@ public final class Fingerprint {
* Updates the digest with a boolean value, correctly handling null.
*/
public Fingerprint addNullableBoolean(Boolean input) {
- addInt(input == null ? -1 : (input.booleanValue() ? 1 : 0));
- return this;
+ return addInt(input == null ? -1 : (input.booleanValue() ? 1 : 0));
}
/**
@@ -118,7 +126,8 @@ public final class Fingerprint {
* @param input the integer with which to update the digest
*/
public Fingerprint addInt(int input) {
- hasher.putInt(input);
+ scratch.putInt(input);
+ updateFromScratch(4);
return this;
}
@@ -128,7 +137,8 @@ public final class Fingerprint {
* @param input the long with which to update the digest
*/
public Fingerprint addLong(long input) {
- hasher.putLong(input);
+ scratch.putLong(input);
+ updateFromScratch(8);
return this;
}
@@ -168,8 +178,7 @@ public final class Fingerprint {
public Fingerprint addString(String input) {
byte[] bytes = input.getBytes(UTF_8);
addInt(bytes.length);
- // Note that Hasher#putString() would not include the length of {@code input}.
- hasher.putBytes(bytes);
+ md5.update(bytes);
return this;
}
@@ -201,7 +210,7 @@ public final class Fingerprint {
for (int i = 0; i < input.length(); i++) {
bytes[i] = (byte) input.charAt(i);
}
- hasher.putBytes(bytes);
+ md5.update(bytes);
return this;
}
@@ -221,8 +230,7 @@ public final class Fingerprint {
* @param input the Path with which to update the digest.
*/
public Fingerprint addPath(PathFragment input) {
- addStringLatin1(input.getPathString());
- return this;
+ return addStringLatin1(input.getPathString());
}
/**
@@ -291,7 +299,51 @@ public final class Fingerprint {
* Reset the Fingerprint for additional use as though previous digesting had not been done.
*/
public void reset() {
- hasher = MD5_HASH_FUNCTION.newHasher();
+ md5.reset();
+ }
+
+ private void updateFromScratch(int numBytes) {
+ md5.update(scratch.array(), 0, numBytes);
+ scratch.clear();
+ }
+
+ 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++) {
+ int n = digest[i];
+ b.append("0123456789abcdef".charAt((n >> 4) & 0xF));
+ b.append("0123456789abcdef".charAt(n & 0xF));
+ }
+ 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 ----------------------------
@@ -303,6 +355,6 @@ public final class Fingerprint {
* @param input the String from which to compute the digest
*/
public static String md5Digest(String input) {
- return MD5_HASH_FUNCTION.hashString(input, UTF_8).toString();
+ return hexDigest(cloneOrCreateMd5().digest(input.getBytes(StandardCharsets.UTF_8)));
}
}