diff options
Diffstat (limited to 'guava/src/com/google/common/hash/MessageDigestHashFunction.java')
-rw-r--r-- | guava/src/com/google/common/hash/MessageDigestHashFunction.java | 193 |
1 files changed, 104 insertions, 89 deletions
diff --git a/guava/src/com/google/common/hash/MessageDigestHashFunction.java b/guava/src/com/google/common/hash/MessageDigestHashFunction.java index 029a494..03c6471 100644 --- a/guava/src/com/google/common/hash/MessageDigestHashFunction.java +++ b/guava/src/com/google/common/hash/MessageDigestHashFunction.java @@ -14,60 +14,37 @@ package com.google.common.hash; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.checkPositionIndexes; -import java.io.Serializable; +import com.google.common.primitives.Chars; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import com.google.common.primitives.Shorts; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; /** - * {@link HashFunction} adapter for {@link MessageDigest} instances. + * {@link HashFunction} adapter for {@link MessageDigest}s. * - * @author Kevin Bourrillion - * @author Dimitris Andreou + * @author kevinb@google.com (Kevin Bourrillion) + * @author andreou@google.com (Dimitris Andreou) */ -final class MessageDigestHashFunction extends AbstractStreamingHashFunction - implements Serializable { - private final MessageDigest prototype; - private final int bytes; - private final boolean supportsClone; - private final String toString; - - MessageDigestHashFunction(String algorithmName, String toString) { - this.prototype = getMessageDigest(algorithmName); - this.bytes = prototype.getDigestLength(); - this.toString = checkNotNull(toString); - this.supportsClone = supportsClone(); - } - - MessageDigestHashFunction(String algorithmName, int bytes, String toString) { - this.toString = checkNotNull(toString); - this.prototype = getMessageDigest(algorithmName); - int maxLength = prototype.getDigestLength(); - checkArgument(bytes >= 4 && bytes <= maxLength, - "bytes (%s) must be >= 4 and < %s", bytes, maxLength); - this.bytes = bytes; - this.supportsClone = supportsClone(); - } +final class MessageDigestHashFunction extends AbstractStreamingHashFunction { + private final String algorithmName; + private final int bits; - private boolean supportsClone() { - try { - prototype.clone(); - return true; - } catch (CloneNotSupportedException e) { - return false; - } + MessageDigestHashFunction(String algorithmName) { + this.algorithmName = algorithmName; + this.bits = getMessageDigest(algorithmName).getDigestLength() * 8; } - @Override public int bits() { - return bytes * Byte.SIZE; - } - - @Override public String toString() { - return toString; + public int bits() { + return bits; } private static MessageDigest getMessageDigest(String algorithmName) { @@ -79,80 +56,118 @@ final class MessageDigestHashFunction extends AbstractStreamingHashFunction } @Override public Hasher newHasher() { - if (supportsClone) { - try { - return new MessageDigestHasher((MessageDigest) prototype.clone(), bytes); - } catch (CloneNotSupportedException e) { - // falls through - } - } - return new MessageDigestHasher(getMessageDigest(prototype.getAlgorithm()), bytes); + return new MessageDigestHasher(getMessageDigest(algorithmName)); } - private static final class SerializedForm implements Serializable { - private final String algorithmName; - private final int bytes; - private final String toString; + private static class MessageDigestHasher implements Hasher { + private final MessageDigest digest; + private final ByteBuffer scratch; // lazy convenience + private boolean done; - private SerializedForm(String algorithmName, int bytes, String toString) { - this.algorithmName = algorithmName; - this.bytes = bytes; - this.toString = toString; + private MessageDigestHasher(MessageDigest digest) { + this.digest = digest; + this.scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); } - private Object readResolve() { - return new MessageDigestHashFunction(algorithmName, bytes, toString); + @Override public Hasher putByte(byte b) { + checkNotDone(); + digest.update(b); + return this; } - private static final long serialVersionUID = 0; - } + @Override public Hasher putBytes(byte[] bytes) { + checkNotDone(); + digest.update(bytes); + return this; + } - Object writeReplace() { - return new SerializedForm(prototype.getAlgorithm(), bytes, toString); - } + @Override public Hasher putBytes(byte[] bytes, int off, int len) { + checkNotDone(); + checkPositionIndexes(off, off + len, bytes.length); + digest.update(bytes, off, len); + return this; + } - /** - * Hasher that updates a message digest. - */ - private static final class MessageDigestHasher extends AbstractByteHasher { + @Override public Hasher putShort(short s) { + checkNotDone(); + scratch.putShort(s); + digest.update(scratch.array(), 0, Shorts.BYTES); + scratch.clear(); + return this; + } - private final MessageDigest digest; - private final int bytes; - private boolean done; + @Override public Hasher putInt(int i) { + checkNotDone(); + scratch.putInt(i); + digest.update(scratch.array(), 0, Ints.BYTES); + scratch.clear(); + return this; + } - private MessageDigestHasher(MessageDigest digest, int bytes) { - this.digest = digest; - this.bytes = bytes; + @Override public Hasher putLong(long l) { + checkNotDone(); + scratch.putLong(l); + digest.update(scratch.array(), 0, Longs.BYTES); + scratch.clear(); + return this; } - @Override - protected void update(byte b) { + @Override public Hasher putFloat(float f) { checkNotDone(); - digest.update(b); + scratch.putFloat(f); + digest.update(scratch.array(), 0, 4); + scratch.clear(); + return this; } - @Override - protected void update(byte[] b) { + @Override public Hasher putDouble(double d) { checkNotDone(); - digest.update(b); + scratch.putDouble(d); + digest.update(scratch.array(), 0, 8); + scratch.clear(); + return this; + } + + @Override public Hasher putBoolean(boolean b) { + return putByte(b ? (byte) 1 : (byte) 0); + } + + @Override public Hasher putChar(char c) { + checkNotDone(); + scratch.putChar(c); + digest.update(scratch.array(), 0, Chars.BYTES); + scratch.clear(); + return this; + } + + @Override public Hasher putString(CharSequence charSequence) { + for (int i = 0; i < charSequence.length(); i++) { + putChar(charSequence.charAt(i)); + } + return this; + } + + @Override public Hasher putString(CharSequence charSequence, Charset charset) { + try { + return putBytes(charSequence.toString().getBytes(charset.name())); + } catch (java.io.UnsupportedEncodingException impossible) { + throw new AssertionError(impossible); + } } - @Override - protected void update(byte[] b, int off, int len) { + @Override public <T> Hasher putObject(T instance, Funnel<? super T> funnel) { checkNotDone(); - digest.update(b, off, len); + funnel.funnel(instance, this); + return this; } private void checkNotDone() { checkState(!done, "Cannot use Hasher after calling #hash() on it"); } - @Override public HashCode hash() { done = true; - return (bytes == digest.getDigestLength()) - ? HashCodes.fromBytesNoCopy(digest.digest()) - : HashCodes.fromBytesNoCopy(Arrays.copyOf(digest.digest(), bytes)); + return HashCodes.fromBytes(digest.digest()); } } } |