2
* This file is a part of Java Bindings for Librhash
3
* Copyright (c) 2011, Sergey Basalaev <sbasalaev@gmail.com>
4
* Librhash is (c) 2011, Alexey S Kravchenko <rhash.admin@gmail.com>
6
* Permission is hereby granted, free of charge, to any person obtaining a copy
7
* of this software and associated documentation files (the "Software"), to deal
8
* in the Software without restriction, including without limitation the rights
9
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
* copies of the Software, and to permit persons to whom the Software is
13
* This library is distributed in the hope that it will be useful, but WITHOUT
14
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
* FOR A PARTICULAR PURPOSE. Use it at your own risk!
21
import java.io.FileInputStream;
22
import java.io.IOException;
23
import java.io.InputStream;
24
import java.nio.charset.Charset;
29
* This class allows you to do incremental hashing for set
30
* of hashing algorithms.
32
* To do hashing <code>RHash</code> instance is first created
33
* and then filled with message chunks using <code>update()</code>
34
* methods. Finally, <code>finish()</code> should be called to end
35
* all calculations and generate digests, which then can be obtained
36
* with <code>getDigest()</code> method. Note, that trying to update
37
* finished <code>RHash</code> has no effect other than throwing
38
* <code>IllegalStateException</code> though you can reuse this class
39
* by calling <code>reset()</code> method, returning it to the state
40
* which was immediately after creating.
42
* To quickly produce message digest for a single message/file
43
* and a single algorithm you may use convenience methods
44
* <code>RHash.computeHash()</code>.
46
* This class is thread safe.
49
public final class RHash {
51
/* == EXCEPTION MESSAGES == */
53
static private final String ERR_FINISHED = "RHash is finished, data update is not possible";
54
static private final String ERR_NOHASH = "No HashTypes specified";
55
static private final String ERR_UNFINISHED = "RHash should be finished before generating Digest";
56
static private final String ERR_WRONGTYPE = "RHash was not created to generate Digest for ";
59
* Computes hash of given range in data.
60
* This method calculates message digest for byte subsequence
61
* in array <code>data</code> starting from <code>data[ofs]</code>
62
* and ending at <code>data[ofs+len-1]</code>.
64
* @param type type of hash algorithm
65
* @param data the bytes to process
66
* @param ofs index of the first byte in array to process
67
* @param len count of bytes to process
68
* @return message digest for specified subarray
69
* @throws NullPointerException
70
* if either <code>type</code> or <code>data</code>
71
* is <code>null</code>
72
* @throws IndexOutOfBoundsException
73
* if <code>ofs < 0</code>, <code>len < 0</code> or
74
* <code>ofs+len > data.length</code>
76
static public Digest computeHash(HashType type, byte[] data, int ofs, int len) {
77
if (type == null || data == null) {
78
throw new NullPointerException();
80
if (ofs < 0 || len < 0 || ofs+len > data.length) {
81
throw new IndexOutOfBoundsException();
83
return new Digest(Bindings.rhash_msg(type.hashId(), data, ofs, len), type);
87
* Computes hash of given data.
89
* @param type type of hash algorithm
90
* @param data the bytes to process
91
* @return message digest for specified array
92
* @throws NullPointerException
93
* if either <code>type</code> or <code>data</code>
94
* is <code>null</code>
96
static public Digest computeHash(HashType type, byte[] data) {
97
return computeHash(type, data, 0, data.length);
101
* Computes hash of given string.
102
* String is encoded into a sequence of bytes
103
* using the specified charset.
105
* @param type type of hash algorithm
106
* @param str the string to process
107
* @param charset charset to encode string
108
* @return message digest for specified string
109
* @throws NullPointerException if any of arguments is <code>null</code>
111
static public Digest computeHash(HashType type, String str, Charset charset) {
112
if (type == null || str == null || charset == null) {
113
throw new NullPointerException();
115
return computeHash(type, str.getBytes(charset));
119
* Computes hash of given string.
120
* String is encoded into a sequence of bytes using the
121
* default platform encoding.
123
* @param type type of hash algorithm
124
* @param str the string to process
125
* @return message digest for specified string
126
* @throws NullPointerException if any of arguments is <code>null</code>
128
static public Digest computeHash(HashType type, String str) {
129
if (type == null || str == null) throw new NullPointerException();
130
return computeHash(type, str.getBytes());
134
* Computes hash of given string.
135
* @param type type of hash algorithm
136
* @param file the file to process
138
* @throws NullPointerException if any of arguments is <code>null</code>
139
* @throws IOException
140
* if an I/O error occurs while hashing
142
static public Digest computeHash(HashType type, File file) throws IOException {
143
if (type == null || file == null) {
144
throw new NullPointerException();
146
RHash hasher = new RHash(type);
147
hasher.update(file).finish();
148
return hasher.getDigest();
151
/** Indicates whether this <code>RHash</code> is finished. */
152
private boolean finished = false;
154
/** Mask of hash_id values. */
155
private final int hash_flags;
157
/** Pointer to the native hash context. */
158
private final long context_ptr;
160
/** Default hash type used in <code>getDigest()</code>. */
161
private final HashType deftype;
164
* Creates new <code>RHash</code> to compute
165
* message digests for given types.
166
* @param types types of hashing algorithms
167
* @throws NullPointerException
168
* if any of arguments is <code>null</code>
169
* @throws IllegalArgumentException
170
* if zero hash types specified
172
public RHash(HashType... types) {
173
if (types.length == 0) {
174
throw new IllegalArgumentException(ERR_NOHASH);
177
HashType def = types[0];
178
for (HashType t : types) {
180
if (def.compareTo(t) > 0) def = t;
183
this.hash_flags = flags;
184
this.context_ptr = Bindings.rhash_init(flags);
188
* Creates new <code>RHash</code> to compute
189
* message digests for given types.
190
* @param types set of hashing types
191
* @throws NullPointerException
192
* if argument is <code>null</code>
193
* @throws IllegalArgumentException
194
* if argument is empty set
196
public RHash(Set<HashType> types) {
197
if (types.isEmpty()) {
198
throw new IllegalArgumentException(ERR_NOHASH);
202
for (HashType t : types) {
204
if (def == null || def.compareTo(t) > 0) def = t;
207
this.hash_flags = flags;
208
this.context_ptr = Bindings.rhash_init(flags);
212
* Updates this <code>RHash</code> with new data chunk.
213
* This method hashes bytes from <code>data[ofs]</code>
214
* through <code>data[ofs+len-1]</code>.
216
* @param data data to be hashed
217
* @param ofs index of the first byte to hash
218
* @param len number of bytes to hash
219
* @return this object
220
* @throws NullPointerException
221
* if <code>data</code> is <code>null</code>
222
* @throws IndexOutOfBoundsException
223
* if <code>ofs < 0</code>, <code>len < 0</code> or
224
* <code>ofs+len > data.length</code>
225
* @throws IllegalStateException
226
* if <code>finish()</code> was called and there were no
227
* subsequent calls of <code>reset()</code>
229
public synchronized RHash update(byte[] data, int ofs, int len) {
231
throw new IllegalStateException(ERR_FINISHED);
233
if (ofs < 0 || len < 0 || ofs+len > data.length) {
234
throw new IndexOutOfBoundsException();
236
Bindings.rhash_update(context_ptr, data, ofs, len);
241
* Updates this <code>RHash</code> with new data chunk.
242
* This method has the same effect as
243
* <pre>update(data, 0, data.length)</pre>
245
* @param data data to be hashed
246
* @return this object
247
* @throws NullPointerException
248
* if <code>data</code> is <code>null</code>
249
* @throws IllegalStateException
250
* if <code>finish()</code> was called and there were no
251
* subsequent calls of <code>reset()</code>
253
public RHash update(byte[] data) {
254
return update(data, 0, data.length);
258
* Updates this <code>RHash</code> with new data chunk.
259
* String is encoded into a sequence of bytes using the
260
* default platform encoding.
262
* @param str string to be hashed
263
* @return this object
264
* @throws NullPointerException
265
* if <code>str</code> is <code>null</code>
266
* @throws IllegalStateException
267
* if <code>finish()</code> was called and there were no
268
* subsequent calls of <code>reset()</code>
270
public RHash update(String str) {
271
return update(str.getBytes());
275
* Updates this <code>RHash</code> with data from given file.
277
* @param file file to be hashed
278
* @return this object
279
* @throws IOException if an I/O error occurs
280
* @throws NullPointerException
281
* if <code>file</code> is <code>null</code>
282
* @throws IllegalStateException
283
* if <code>finish()</code> was called and there were no
284
* subsequent calls of <code>reset()</code>
286
public synchronized RHash update(File file) throws IOException {
288
throw new IllegalStateException(ERR_FINISHED);
290
InputStream in = new FileInputStream(file);
291
byte[] buf = new byte[8192]; //shouldn't we avoid magic numbers?
292
int len = in.read(buf);
294
this.update(buf, 0, len);
302
* Finishes calculation of hash codes.
303
* Does nothing if <code>RHash</code> is already finished.
305
public synchronized void finish() {
307
Bindings.rhash_final(context_ptr);
313
* Resets this <code>RHash</code> to initial state.
314
* The <code>RHash</code> becomes available to process
315
* new data chunks. Note, that this method returns
316
* <code>RHash</code> to the state after creating the
317
* object, NOT the state when hashing continues.
318
* Therefore, all previously calculated hashes are lost
319
* and process starts from the very beginning.
321
public synchronized void reset() {
322
Bindings.rhash_reset(context_ptr);
327
* Tests whether this <code>RHash</code> is finished or not.
329
* <code>false</code> if this <code>RHash</code> is ready to
330
* receive new data for hashing;
331
* <code>true</code> if hash calculations are finished
333
public boolean isFinished() {
338
* Returns digest for given hash type.
340
* @param type hash type
341
* @return <code>Digest</code> for processed data
342
* @throws NullPointerException
343
* if <code>type</code> is <code>null</code>
344
* @throws IllegalStateException
345
* if this <code>RHash</code> is not finished
346
* @throws IllegalArgumentException
347
* if this <code>RHash</code> was not created to calculate
348
* hash for specified algorithm
350
public Digest getDigest(HashType type) {
352
throw new NullPointerException();
355
throw new IllegalStateException(ERR_UNFINISHED);
357
if ((hash_flags & type.hashId()) == 0) {
358
throw new IllegalArgumentException(ERR_WRONGTYPE+type);
360
return new Digest(Bindings.rhash_print(context_ptr, type.hashId()), type);
364
* Returns digest for processed data.
365
* If more than one hashing type was passed to the
366
* <code>RHash</code> constructor, then the least
367
* hash type (in the order induced by
368
* {@link Enum#compareTo(Enum) compareTo()}) is used.
370
* @return <code>Digest</code> for processed data
371
* @throws IllegalStateException
372
* if this <code>RHash</code> is not finished
374
public Digest getDigest() {
375
return getDigest(deftype);
379
* Called by garbage collector to free native resources.
382
protected void finalize() {
383
Bindings.rhash_free(context_ptr);