001 /*--------------------------------------------------------------------------+ 002 $Id: MD5Digest.java 26722 2010-03-15 08:00:59Z heineman $ 003 | | 004 | Copyright 2005-2010 Technische Universitaet Muenchen | 005 | | 006 | Licensed under the Apache License, Version 2.0 (the "License"); | 007 | you may not use this file except in compliance with the License. | 008 | You may obtain a copy of the License at | 009 | | 010 | http://www.apache.org/licenses/LICENSE-2.0 | 011 | | 012 | Unless required by applicable law or agreed to in writing, software | 013 | distributed under the License is distributed on an "AS IS" BASIS, | 014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 015 | See the License for the specific language governing permissions and | 016 | limitations under the License. | 017 +--------------------------------------------------------------------------*/ 018 package edu.tum.cs.commons.digest; 019 020 import java.io.IOException; 021 import java.io.ObjectInputStream; 022 import java.io.ObjectOutputStream; 023 import java.io.Serializable; 024 import java.security.MessageDigest; 025 import java.util.Arrays; 026 027 import edu.tum.cs.commons.assertion.CCSMPre; 028 import edu.tum.cs.commons.string.StringUtils; 029 030 /** 031 * An MD5 digest. This is just a thin thin wrapper around a byte array with some 032 * convenience methods and {@link #hashCode()}, {@link #equals(Object)} and 033 * {@link #compareTo(MD5Digest)} implemented correctly. This class is used 034 * instead of plain strings to save both memory and (some) execution time. The 035 * class is immutable. Custom (de)serialization is provided to make this 036 * efficient to use in storage or RMI senarios. 037 * 038 * @author hummelb 039 * @author $Author: heineman $ 040 * @version $Rev: 26722 $ 041 * @levd.rating GREEN Hash: 1F3799422871C3DC0A88A5AF75FE6BCD 042 */ 043 public final class MD5Digest implements Serializable, Comparable<MD5Digest> { 044 045 /** The digest. */ 046 private byte[] digest; 047 048 /** Number of bytes in an MD5 sum. */ 049 public static final int MD5_BYTES = 16; 050 051 /** 052 * Constructor. This calls {@link MessageDigest#digest()}, so the digester 053 * will be reset afterwards. 054 */ 055 public MD5Digest(MessageDigest digester) { 056 digest = digester.digest(); 057 CCSMPre.isTrue(digest.length == MD5_BYTES, 058 "Invalid digester used; not MD5"); 059 } 060 061 /** Constructor. */ 062 public MD5Digest(byte[] digest) { 063 CCSMPre.isTrue(digest.length == MD5_BYTES, 064 "Invalid size of MD5 digest!"); 065 this.digest = digest.clone(); 066 } 067 068 /** 069 * Inserts the digest data into the given MD. This method is used to rehash 070 * multiple hashes. 071 * <p> 072 * This method is provided instead of a getter, to keep this immutable. 073 */ 074 public void insertIntoDigester(MessageDigest md) { 075 md.update(digest); 076 } 077 078 /** {@inheritDoc} */ 079 @Override 080 public int hashCode() { 081 return Arrays.hashCode(digest); 082 } 083 084 /** 085 * Calculates and returns a hashcode that only depends on the first 3 bytes. 086 */ 087 public int partialHashCode() { 088 return digest[0] | (digest[1] << 8) | (digest[2] << 16); 089 } 090 091 /** {@inheritDoc} */ 092 @Override 093 public boolean equals(Object o) { 094 if (!(o instanceof MD5Digest)) { 095 return false; 096 } 097 return Arrays.equals(digest, ((MD5Digest) o).digest); 098 } 099 100 /** {@inheritDoc} */ 101 public int compareTo(MD5Digest o) { 102 if (o == null) { 103 return -1; 104 } 105 106 int lengthDelta = digest.length - o.digest.length; 107 if (lengthDelta != 0) { 108 return lengthDelta; 109 } 110 111 for (int i = 0; i < digest.length; ++i) { 112 int delta = digest[i] - o.digest[i]; 113 if (delta != 0) { 114 return delta; 115 } 116 } 117 118 return 0; 119 } 120 121 /** Returns a copy of the internal byte representation. */ 122 public byte[] getBytes() { 123 return digest.clone(); 124 } 125 126 /** {@inheritDoc} */ 127 @Override 128 public String toString() { 129 return StringUtils.encodeAsHex(digest); 130 } 131 132 /** Custom serialization for MD5 hashes. */ 133 private void writeObject(ObjectOutputStream out) throws IOException { 134 out.writeByte(digest.length); 135 out.write(digest); 136 } 137 138 /** Custom deserialization for MD5 hashes. */ 139 private void readObject(ObjectInputStream in) throws IOException { 140 int size = in.readByte(); 141 digest = new byte[size]; 142 int pos = 0; 143 while (pos < size) { 144 pos += in.read(digest, pos, size - pos); 145 } 146 } 147 148 /** Comparator for {@link MD5Digest}. */ 149 public static class Comparator implements java.util.Comparator<MD5Digest>, 150 Serializable { 151 152 /** {@inheritDoc} */ 153 @Override 154 public int compare(MD5Digest o1, MD5Digest o2) { 155 return o1.compareTo(o2); 156 } 157 } 158 }