diff options
author | 2016-11-30 18:59:59 +0000 | |
---|---|---|
committer | 2016-12-01 10:15:39 +0000 | |
commit | 43ad03290228adc8187853f83be073119c6f9aaa (patch) | |
tree | 12192e2ad39db9c10141e4f91f98fc43f832b814 /src/main | |
parent | 1f95a4e35b19bea98dbaca3130729c2465b85f95 (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')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/util/Fingerprint.java | 110 |
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))); } } |