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 }