MessageDigestHashFunction.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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.checkState; 18import static com.google.common.base.Preconditions.checkPositionIndexes; 19 20import com.google.common.primitives.Chars; 21import com.google.common.primitives.Ints; 22import com.google.common.primitives.Longs; 23import com.google.common.primitives.Shorts; 24 25import java.nio.ByteBuffer; 26import java.nio.ByteOrder; 27import java.nio.charset.Charset; 28import java.security.MessageDigest; 29import java.security.NoSuchAlgorithmException; 30 31/** 32 * {@link HashFunction} adapter for {@link MessageDigest}s. 33 * 34 * @author kevinb@google.com (Kevin Bourrillion) 35 * @author andreou@google.com (Dimitris Andreou) 36 */ 37final class MessageDigestHashFunction extends AbstractStreamingHashFunction { 38 private final String algorithmName; 39 private final int bits; 40 41 MessageDigestHashFunction(String algorithmName) { 42 this.algorithmName = algorithmName; 43 this.bits = getMessageDigest(algorithmName).getDigestLength() * 8; 44 } 45 46 public int bits() { 47 return bits; 48 } 49 50 private static MessageDigest getMessageDigest(String algorithmName) { 51 try { 52 return MessageDigest.getInstance(algorithmName); 53 } catch (NoSuchAlgorithmException e) { 54 throw new AssertionError(e); 55 } 56 } 57 58 @Override public Hasher newHasher() { 59 return new MessageDigestHasher(getMessageDigest(algorithmName)); 60 } 61 62 private static class MessageDigestHasher implements Hasher { 63 private final MessageDigest digest; 64 private final ByteBuffer scratch; // lazy convenience 65 private boolean done; 66 67 private MessageDigestHasher(MessageDigest digest) { 68 this.digest = digest; 69 this.scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); 70 } 71 72 @Override public Hasher putByte(byte b) { 73 checkNotDone(); 74 digest.update(b); 75 return this; 76 } 77 78 @Override public Hasher putBytes(byte[] bytes) { 79 checkNotDone(); 80 digest.update(bytes); 81 return this; 82 } 83 84 @Override public Hasher putBytes(byte[] bytes, int off, int len) { 85 checkNotDone(); 86 checkPositionIndexes(off, off + len, bytes.length); 87 digest.update(bytes, off, len); 88 return this; 89 } 90 91 @Override public Hasher putShort(short s) { 92 checkNotDone(); 93 scratch.putShort(s); 94 digest.update(scratch.array(), 0, Shorts.BYTES); 95 scratch.clear(); 96 return this; 97 } 98 99 @Override public Hasher putInt(int i) { 100 checkNotDone(); 101 scratch.putInt(i); 102 digest.update(scratch.array(), 0, Ints.BYTES); 103 scratch.clear(); 104 return this; 105 } 106 107 @Override public Hasher putLong(long l) { 108 checkNotDone(); 109 scratch.putLong(l); 110 digest.update(scratch.array(), 0, Longs.BYTES); 111 scratch.clear(); 112 return this; 113 } 114 115 @Override public Hasher putFloat(float f) { 116 checkNotDone(); 117 scratch.putFloat(f); 118 digest.update(scratch.array(), 0, 4); 119 scratch.clear(); 120 return this; 121 } 122 123 @Override public Hasher putDouble(double d) { 124 checkNotDone(); 125 scratch.putDouble(d); 126 digest.update(scratch.array(), 0, 8); 127 scratch.clear(); 128 return this; 129 } 130 131 @Override public Hasher putBoolean(boolean b) { 132 return putByte(b ? (byte) 1 : (byte) 0); 133 } 134 135 @Override public Hasher putChar(char c) { 136 checkNotDone(); 137 scratch.putChar(c); 138 digest.update(scratch.array(), 0, Chars.BYTES); 139 scratch.clear(); 140 return this; 141 } 142 143 @Override public Hasher putString(CharSequence charSequence) { 144 for (int i = 0; i < charSequence.length(); i++) { 145 putChar(charSequence.charAt(i)); 146 } 147 return this; 148 } 149 150 @Override public Hasher putString(CharSequence charSequence, Charset charset) { 151 try { 152 return putBytes(charSequence.toString().getBytes(charset.name())); 153 } catch (java.io.UnsupportedEncodingException impossible) { 154 throw new AssertionError(impossible); 155 } 156 } 157 158 @Override public <T> Hasher putObject(T instance, Funnel<? super T> funnel) { 159 checkNotDone(); 160 funnel.funnel(instance, this); 161 return this; 162 } 163 164 private void checkNotDone() { 165 checkState(!done, "Cannot use Hasher after calling #hash() on it"); 166 } 167 168 public HashCode hash() { 169 done = true; 170 return HashCodes.fromBytes(digest.digest()); 171 } 172 } 173} 174