~ubuntu-branches/ubuntu/precise/rhash/precise

« back to all changes in this revision

Viewing changes to bindings/java/src/org/sf/rhash/RHash.java

  • Committer: Package Import Robot
  • Author(s): Aleksey Kravchenko
  • Date: 2011-09-14 23:50:14 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: package-import@ubuntu.com-20110914235014-m9mk3eg7e6rah17x
Tags: 1.2.8-1
* New upstream release version 1.2.8
 - language bindings for Java, Perl, Python, Ruby
 - Russian translation
 - fixed recommended libssl1.0.0 dependency
 - patched broken re-compilation
 - fixed a mistype in perl documentation
 - control: updated Vcs-Git field
 - copyright: corrected Format field, added Upstream-Contact
 - copyright: updated Copyright field, mentioned another developer

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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>
 
5
 * 
 
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
 
11
 * furnished to do so.
 
12
 * 
 
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!
 
16
 */
 
17
 
 
18
package org.sf.rhash;
 
19
 
 
20
import java.io.File;
 
21
import java.io.FileInputStream;
 
22
import java.io.IOException;
 
23
import java.io.InputStream;
 
24
import java.nio.charset.Charset;
 
25
import java.util.Set;
 
26
 
 
27
/**
 
28
 * Incremental hasher.
 
29
 * This class allows you to do incremental hashing for set
 
30
 * of hashing algorithms.
 
31
 * <p>
 
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.
 
41
 * </p><p>
 
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>.
 
45
 * </p><p>
 
46
 * This class is thread safe.
 
47
 * </p>
 
48
 */
 
49
public final class RHash {
 
50
 
 
51
        /* == EXCEPTION MESSAGES == */
 
52
 
 
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 ";
 
57
 
 
58
        /**
 
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>.
 
63
         * 
 
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 &lt; 0</code>, <code>len &lt; 0</code> or
 
74
         *   <code>ofs+len &gt; data.length</code>
 
75
         */
 
76
        static public Digest computeHash(HashType type, byte[] data, int ofs, int len) {
 
77
                if (type == null || data == null) {
 
78
                        throw new NullPointerException();
 
79
                }
 
80
                if (ofs < 0 || len < 0 || ofs+len > data.length) {
 
81
                        throw new IndexOutOfBoundsException();
 
82
                }
 
83
                return new Digest(Bindings.rhash_msg(type.hashId(), data, ofs, len), type);
 
84
        }
 
85
        
 
86
        /**
 
87
         * Computes hash of given data.
 
88
         * 
 
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>
 
95
         */
 
96
        static public Digest computeHash(HashType type, byte[] data) {
 
97
                return computeHash(type, data, 0, data.length);
 
98
        }
 
99
 
 
100
        /**
 
101
         * Computes hash of given string.
 
102
         * String is encoded into a sequence of bytes
 
103
         * using the specified charset.
 
104
         *
 
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>
 
110
         */
 
111
        static public Digest computeHash(HashType type, String str, Charset charset) {
 
112
                if (type == null || str == null || charset == null) {
 
113
                        throw new NullPointerException();
 
114
                }
 
115
                return computeHash(type, str.getBytes(charset));
 
116
        }
 
117
 
 
118
        /**
 
119
         * Computes hash of given string.
 
120
         * String is encoded into a sequence of bytes using the
 
121
         * default platform encoding.
 
122
         *
 
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>
 
127
         */
 
128
        static public Digest computeHash(HashType type, String str) {
 
129
                if (type == null || str == null) throw new NullPointerException();
 
130
                return computeHash(type, str.getBytes());
 
131
        }
 
132
 
 
133
        /**
 
134
         * Computes hash of given string.
 
135
         * @param  type  type of hash algorithm
 
136
         * @param  file  the file to process
 
137
         * @return  data hash
 
138
         * @throws NullPointerException if any of arguments is <code>null</code>
 
139
         * @throws IOException
 
140
         *   if an I/O error occurs while hashing
 
141
         */
 
142
        static public Digest computeHash(HashType type, File file) throws IOException {
 
143
                if (type == null || file == null) {
 
144
                        throw new NullPointerException();
 
145
                }
 
146
                RHash hasher = new RHash(type);
 
147
                hasher.update(file).finish();
 
148
                return hasher.getDigest();
 
149
        }
 
150
 
 
151
        /** Indicates whether this <code>RHash</code> is finished. */
 
152
        private boolean finished = false;
 
153
 
 
154
        /** Mask of hash_id values. */
 
155
        private final int hash_flags;
 
156
 
 
157
        /** Pointer to the native hash context. */
 
158
        private final long context_ptr;
 
159
 
 
160
        /** Default hash type used in <code>getDigest()</code>. */
 
161
        private final HashType deftype;
 
162
 
 
163
        /**
 
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
 
171
         */
 
172
        public RHash(HashType... types) {
 
173
                if (types.length == 0) {
 
174
                        throw new IllegalArgumentException(ERR_NOHASH);
 
175
                }
 
176
                int flags = 0;
 
177
                HashType def = types[0];
 
178
                for (HashType t : types) {
 
179
                        flags |= t.hashId();
 
180
                        if (def.compareTo(t) > 0) def = t;
 
181
                }
 
182
                this.deftype = def;
 
183
                this.hash_flags = flags;
 
184
                this.context_ptr = Bindings.rhash_init(flags);
 
185
        }
 
186
 
 
187
        /**
 
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
 
195
         */
 
196
        public RHash(Set<HashType> types) {
 
197
                if (types.isEmpty()) {
 
198
                        throw new IllegalArgumentException(ERR_NOHASH);
 
199
                }
 
200
                int flags = 0;
 
201
                HashType def = null;
 
202
                for (HashType t : types) {
 
203
                        flags |= t.hashId();
 
204
                        if (def == null || def.compareTo(t) > 0) def = t;
 
205
                }
 
206
                this.deftype = def;
 
207
                this.hash_flags = flags;
 
208
                this.context_ptr = Bindings.rhash_init(flags);
 
209
        }
 
210
 
 
211
        /**
 
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>.
 
215
         * 
 
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 &lt; 0</code>, <code>len &lt; 0</code> or
 
224
         *   <code>ofs+len &gt; data.length</code>
 
225
         * @throws IllegalStateException
 
226
         *   if <code>finish()</code> was called and there were no
 
227
         *   subsequent calls of <code>reset()</code>
 
228
         */
 
229
        public synchronized RHash update(byte[] data, int ofs, int len) {
 
230
                if (finished) {
 
231
                        throw new IllegalStateException(ERR_FINISHED);
 
232
                }
 
233
                if (ofs < 0 || len < 0 || ofs+len > data.length) {
 
234
                        throw new IndexOutOfBoundsException();
 
235
                }
 
236
                Bindings.rhash_update(context_ptr, data, ofs, len);
 
237
                return this;
 
238
        }
 
239
 
 
240
        /**
 
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>
 
244
         *
 
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>
 
252
         */
 
253
        public RHash update(byte[] data) {
 
254
                return update(data, 0, data.length);
 
255
        }
 
256
 
 
257
        /**
 
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.
 
261
         * 
 
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>
 
269
         */
 
270
        public RHash update(String str) {
 
271
                return update(str.getBytes());
 
272
        }
 
273
 
 
274
        /**
 
275
         * Updates this <code>RHash</code> with data from given file.
 
276
         * 
 
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>
 
285
         */
 
286
        public synchronized RHash update(File file) throws IOException {
 
287
                if (finished) {
 
288
                        throw new IllegalStateException(ERR_FINISHED);
 
289
                }
 
290
                InputStream in = new FileInputStream(file);
 
291
                byte[] buf = new byte[8192];  //shouldn't we avoid magic numbers?
 
292
                int len = in.read(buf);
 
293
                while (len > 0) {
 
294
                        this.update(buf, 0, len);
 
295
                        len = in.read(buf);
 
296
                }
 
297
                in.close();
 
298
                return this;
 
299
        }
 
300
 
 
301
        /**
 
302
         * Finishes calculation of hash codes.
 
303
         * Does nothing if <code>RHash</code> is already finished.
 
304
         */
 
305
        public synchronized void finish() {
 
306
                if (!finished) {
 
307
                        Bindings.rhash_final(context_ptr);
 
308
                        finished = true;
 
309
                }
 
310
        }
 
311
 
 
312
        /**
 
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.
 
320
         */
 
321
        public synchronized void reset() {
 
322
                Bindings.rhash_reset(context_ptr);
 
323
                finished = false;
 
324
        }
 
325
 
 
326
        /**
 
327
         * Tests whether this <code>RHash</code> is finished or not.
 
328
         * @return
 
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
 
332
         */
 
333
        public boolean isFinished() {
 
334
                return finished;
 
335
        }
 
336
 
 
337
        /**
 
338
         * Returns digest for given hash type.
 
339
         * 
 
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
 
349
         */
 
350
        public Digest getDigest(HashType type) {
 
351
                if (type == null) {
 
352
                        throw new NullPointerException();
 
353
                }
 
354
                if (!finished) {
 
355
                        throw new IllegalStateException(ERR_UNFINISHED);
 
356
                }
 
357
                if ((hash_flags & type.hashId()) == 0) {
 
358
                        throw new IllegalArgumentException(ERR_WRONGTYPE+type);
 
359
                }
 
360
                return new Digest(Bindings.rhash_print(context_ptr, type.hashId()), type);
 
361
        }
 
362
 
 
363
        /**
 
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.
 
369
         * 
 
370
         * @return <code>Digest</code> for processed data
 
371
         * @throws IllegalStateException
 
372
         *   if this <code>RHash</code> is not finished
 
373
         */
 
374
        public Digest getDigest() {
 
375
                return getDigest(deftype);
 
376
        }
 
377
 
 
378
        /**
 
379
         * Called by garbage collector to free native resources.
 
380
         */
 
381
        @Override
 
382
        protected void finalize() {
 
383
                Bindings.rhash_free(context_ptr);
 
384
        }
 
385
}