1/* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package com.google.common.hash; 16 17import static com.google.common.base.Preconditions.checkArgument; 18import static com.google.common.base.Preconditions.checkNotNull; 19import static com.google.common.base.Preconditions.checkState; 20 21import java.io.Serializable; 22import java.security.MessageDigest; 23import java.security.NoSuchAlgorithmException; 24import java.util.Arrays; 25 26/** 27 * {@link HashFunction} adapter for {@link MessageDigest} instances. 28 * 29 * @author Kevin Bourrillion 30 * @author Dimitris Andreou 31 */ 32final class MessageDigestHashFunction extends AbstractStreamingHashFunction 33 implements Serializable { 34 private final MessageDigest prototype; 35 private final int bytes; 36 private final boolean supportsClone; 37 private final String toString; 38 39 MessageDigestHashFunction(String algorithmName, String toString) { 40 this.prototype = getMessageDigest(algorithmName); 41 this.bytes = prototype.getDigestLength(); 42 this.toString = checkNotNull(toString); 43 this.supportsClone = supportsClone(); 44 } 45 46 MessageDigestHashFunction(String algorithmName, int bytes, String toString) { 47 this.toString = checkNotNull(toString); 48 this.prototype = getMessageDigest(algorithmName); 49 int maxLength = prototype.getDigestLength(); 50 checkArgument(bytes >= 4 && bytes <= maxLength, 51 "bytes (%s) must be >= 4 and < %s", bytes, maxLength); 52 this.bytes = bytes; 53 this.supportsClone = supportsClone(); 54 } 55 56 private boolean supportsClone() { 57 try { 58 prototype.clone(); 59 return true; 60 } catch (CloneNotSupportedException e) { 61 return false; 62 } 63 } 64 65 @Override public int bits() { 66 return bytes * Byte.SIZE; 67 } 68 69 @Override public String toString() { 70 return toString; 71 } 72 73 private static MessageDigest getMessageDigest(String algorithmName) { 74 try { 75 return MessageDigest.getInstance(algorithmName); 76 } catch (NoSuchAlgorithmException e) { 77 throw new AssertionError(e); 78 } 79 } 80 81 @Override public Hasher newHasher() { 82 if (supportsClone) { 83 try { 84 return new MessageDigestHasher((MessageDigest) prototype.clone(), bytes); 85 } catch (CloneNotSupportedException e) { 86 // falls through 87 } 88 } 89 return new MessageDigestHasher(getMessageDigest(prototype.getAlgorithm()), bytes); 90 } 91 92 private static final class SerializedForm implements Serializable { 93 private final String algorithmName; 94 private final int bytes; 95 private final String toString; 96 97 private SerializedForm(String algorithmName, int bytes, String toString) { 98 this.algorithmName = algorithmName; 99 this.bytes = bytes; 100 this.toString = toString; 101 } 102 103 private Object readResolve() { 104 return new MessageDigestHashFunction(algorithmName, bytes, toString); 105 } 106 107 private static final long serialVersionUID = 0; 108 } 109 110 Object writeReplace() { 111 return new SerializedForm(prototype.getAlgorithm(), bytes, toString); 112 } 113 114 /** 115 * Hasher that updates a message digest. 116 */ 117 private static final class MessageDigestHasher extends AbstractByteHasher { 118 119 private final MessageDigest digest; 120 private final int bytes; 121 private boolean done; 122 123 private MessageDigestHasher(MessageDigest digest, int bytes) { 124 this.digest = digest; 125 this.bytes = bytes; 126 } 127 128 @Override 129 protected void update(byte b) { 130 checkNotDone(); 131 digest.update(b); 132 } 133 134 @Override 135 protected void update(byte[] b) { 136 checkNotDone(); 137 digest.update(b); 138 } 139 140 @Override 141 protected void update(byte[] b, int off, int len) { 142 checkNotDone(); 143 digest.update(b, off, len); 144 } 145 146 private void checkNotDone() { 147 checkState(!done, "Cannot re-use a Hasher after calling hash() on it"); 148 } 149 150 @Override 151 public HashCode hash() { 152 checkNotDone(); 153 done = true; 154 return (bytes == digest.getDigestLength()) 155 ? HashCode.fromBytesNoCopy(digest.digest()) 156 : HashCode.fromBytesNoCopy(Arrays.copyOf(digest.digest(), bytes)); 157 } 158 } 159} 160