~ubuntu-branches/ubuntu/trusty/rhash/trusty

« 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: 2013-09-16 19:48:55 UTC
  • mfrom: (1.2.1) (16.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20130916194855-sengvtinypyl41ld
Tags: 1.3.0-1
* New upstream release version 1.3.0
 - switched the package back to unstable

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-2012, Sergey Basalaev <sbasalaev@gmail.com>
4
 
 * Librhash is (c) 2011-2012, Aleksey 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.io.UnsupportedEncodingException;
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  encoding  encoding to use
108
 
         * @return  message digest for specified string
109
 
         * @throws NullPointerException if any of arguments is <code>null</code>
110
 
         * @throws UnsupportedEncodingException if specified encoding is not supported
111
 
         */
112
 
        static public Digest computeHash(HashType type, String str, String encoding)
113
 
                        throws UnsupportedEncodingException {
114
 
                if (type == null || str == null || encoding == null) {
115
 
                        throw new NullPointerException();
116
 
                }
117
 
                return computeHash(type, str.getBytes(encoding));
118
 
        }
119
 
 
120
 
        /**
121
 
         * Computes hash of given string.
122
 
         * String is encoded into a sequence of bytes using the
123
 
         * default platform encoding.
124
 
         *
125
 
         * @param  type  type of hash algorithm
126
 
         * @param  str   the string to process
127
 
         * @return  message digest for specified string
128
 
         * @throws NullPointerException if any of arguments is <code>null</code>
129
 
         */
130
 
        static public Digest computeHash(HashType type, String str) {
131
 
                if (type == null || str == null) throw new NullPointerException();
132
 
                return computeHash(type, str.getBytes());
133
 
        }
134
 
 
135
 
        /**
136
 
         * Computes hash of given string.
137
 
         * @param  type  type of hash algorithm
138
 
         * @param  file  the file to process
139
 
         * @return  data hash
140
 
         * @throws NullPointerException if any of arguments is <code>null</code>
141
 
         * @throws IOException
142
 
         *   if an I/O error occurs while hashing
143
 
         */
144
 
        static public Digest computeHash(HashType type, File file) throws IOException {
145
 
                if (type == null || file == null) {
146
 
                        throw new NullPointerException();
147
 
                }
148
 
                RHash hasher = new RHash(type);
149
 
                hasher.update(file).finish();
150
 
                return hasher.getDigest();
151
 
        }
152
 
        
153
 
        /**
154
 
         * Produces magnet link for specified file with given hashes.
155
 
         *
156
 
         * @param  filename   the file to generate magnet for
157
 
         * @param  types      types of hashing algorithms
158
 
         */
159
 
        static public String getMagnetFor(String filename, HashType... types) throws IOException {
160
 
                RHash hasher = new RHash(types);
161
 
                hasher.update(new File(filename)).finish();
162
 
                return hasher.getMagnet(filename);
163
 
        }
164
 
        
165
 
        /**
166
 
         * Produces magnet link for specified file with given hashes.
167
 
         *
168
 
         * @param  filename   the file to generate magnet for
169
 
         * @param  types      set of hashing types
170
 
         */
171
 
        static public String getMagnetFor(String filename, Set<HashType> types) throws IOException {
172
 
                RHash hasher = new RHash(types);
173
 
                hasher.update(new File(filename)).finish();
174
 
                return hasher.getMagnet(filename);
175
 
        }
176
 
 
177
 
        /** Indicates whether this <code>RHash</code> is finished. */
178
 
        private boolean finished = false;
179
 
 
180
 
        /** Mask of hash_id values. */
181
 
        private final int hash_flags;
182
 
 
183
 
        /** Pointer to the native hash context. */
184
 
        private final long context_ptr;
185
 
 
186
 
        /** Default hash type used in <code>getDigest()</code>. */
187
 
        private final HashType deftype;
188
 
 
189
 
        /**
190
 
         * Creates new <code>RHash</code> to compute
191
 
         * message digests for given types.
192
 
         * @param  types  types of hashing algorithms
193
 
         * @throws NullPointerException
194
 
         *   if any of arguments is <code>null</code>
195
 
         * @throws IllegalArgumentException
196
 
         *   if zero hash types specified
197
 
         */
198
 
        public RHash(HashType... types) {
199
 
                if (types.length == 0) {
200
 
                        throw new IllegalArgumentException(ERR_NOHASH);
201
 
                }
202
 
                int flags = 0;
203
 
                HashType def = types[0];
204
 
                for (HashType t : types) {
205
 
                        flags |= t.hashId();
206
 
                        if (def.compareTo(t) > 0) def = t;
207
 
                }
208
 
                this.deftype = def;
209
 
                this.hash_flags = flags;
210
 
                this.context_ptr = Bindings.rhash_init(flags);
211
 
        }
212
 
 
213
 
        /**
214
 
         * Creates new <code>RHash</code> to compute
215
 
         * message digests for given types.
216
 
         * @param  types  set of hashing types
217
 
         * @throws NullPointerException
218
 
         *   if argument is <code>null</code>
219
 
         * @throws IllegalArgumentException
220
 
         *   if argument is empty set
221
 
         */
222
 
        public RHash(Set<HashType> types) {
223
 
                if (types.isEmpty()) {
224
 
                        throw new IllegalArgumentException(ERR_NOHASH);
225
 
                }
226
 
                int flags = 0;
227
 
                HashType def = null;
228
 
                for (HashType t : types) {
229
 
                        flags |= t.hashId();
230
 
                        if (def == null || def.compareTo(t) > 0) def = t;
231
 
                }
232
 
                this.deftype = def;
233
 
                this.hash_flags = flags;
234
 
                this.context_ptr = Bindings.rhash_init(flags);
235
 
        }
236
 
 
237
 
        /**
238
 
         * Updates this <code>RHash</code> with new data chunk.
239
 
         * This method hashes bytes from <code>data[ofs]</code>
240
 
         * through <code>data[ofs+len-1]</code>.
241
 
         * 
242
 
         * @param  data  data to be hashed
243
 
         * @param  ofs   index of the first byte to hash
244
 
         * @param  len   number of bytes to hash
245
 
         * @return  this object
246
 
         * @throws NullPointerException
247
 
         *   if <code>data</code> is <code>null</code>
248
 
         * @throws IndexOutOfBoundsException
249
 
         *   if <code>ofs &lt; 0</code>, <code>len &lt; 0</code> or
250
 
         *   <code>ofs+len &gt; data.length</code>
251
 
         * @throws IllegalStateException
252
 
         *   if <code>finish()</code> was called and there were no
253
 
         *   subsequent calls of <code>reset()</code>
254
 
         */
255
 
        public synchronized RHash update(byte[] data, int ofs, int len) {
256
 
                if (finished) {
257
 
                        throw new IllegalStateException(ERR_FINISHED);
258
 
                }
259
 
                if (ofs < 0 || len < 0 || ofs+len > data.length) {
260
 
                        throw new IndexOutOfBoundsException();
261
 
                }
262
 
                Bindings.rhash_update(context_ptr, data, ofs, len);
263
 
                return this;
264
 
        }
265
 
 
266
 
        /**
267
 
         * Updates this <code>RHash</code> with new data chunk.
268
 
         * This method has the same effect as
269
 
         * <pre>update(data, 0, data.length)</pre>
270
 
         *
271
 
         * @param  data  data to be hashed
272
 
         * @return  this object
273
 
         * @throws NullPointerException
274
 
         *   if <code>data</code> is <code>null</code>
275
 
         * @throws IllegalStateException
276
 
         *   if <code>finish()</code> was called and there were no
277
 
         *   subsequent calls of <code>reset()</code>
278
 
         */
279
 
        public RHash update(byte[] data) {
280
 
                return update(data, 0, data.length);
281
 
        }
282
 
 
283
 
        /**
284
 
         * Updates this <code>RHash</code> with new data chunk.
285
 
         * String is encoded into a sequence of bytes using the
286
 
         * default platform encoding.
287
 
         * 
288
 
         * @param str  string to be hashed
289
 
         * @return this object
290
 
         * @throws NullPointerException
291
 
         *   if <code>str</code> is <code>null</code>
292
 
         * @throws IllegalStateException
293
 
         *   if <code>finish()</code> was called and there were no
294
 
         *   subsequent calls of <code>reset()</code>
295
 
         */
296
 
        public RHash update(String str) {
297
 
                return update(str.getBytes());
298
 
        }
299
 
 
300
 
        /**
301
 
         * Updates this <code>RHash</code> with data from given file.
302
 
         * 
303
 
         * @param  file  file to be hashed
304
 
         * @return this object
305
 
         * @throws IOException if an I/O error occurs
306
 
         * @throws NullPointerException
307
 
         *   if <code>file</code> is <code>null</code>
308
 
         * @throws IllegalStateException
309
 
         *   if <code>finish()</code> was called and there were no
310
 
         *   subsequent calls of <code>reset()</code>
311
 
         */
312
 
        public synchronized RHash update(File file) throws IOException {
313
 
                if (finished) {
314
 
                        throw new IllegalStateException(ERR_FINISHED);
315
 
                }
316
 
                InputStream in = new FileInputStream(file);
317
 
                byte[] buf = new byte[8192];  //shouldn't we avoid magic numbers?
318
 
                int len = in.read(buf);
319
 
                while (len > 0) {
320
 
                        this.update(buf, 0, len);
321
 
                        len = in.read(buf);
322
 
                }
323
 
                in.close();
324
 
                return this;
325
 
        }
326
 
 
327
 
        /**
328
 
         * Finishes calculation of hash codes.
329
 
         * Does nothing if <code>RHash</code> is already finished.
330
 
         */
331
 
        public synchronized void finish() {
332
 
                if (!finished) {
333
 
                        Bindings.rhash_final(context_ptr);
334
 
                        finished = true;
335
 
                }
336
 
        }
337
 
 
338
 
        /**
339
 
         * Resets this <code>RHash</code> to initial state.
340
 
         * The <code>RHash</code> becomes available to process
341
 
         * new data chunks. Note, that this method returns
342
 
         * <code>RHash</code> to the state after creating the
343
 
         * object, NOT the state when hashing continues.
344
 
         * Therefore, all previously calculated hashes are lost
345
 
         * and process starts from the very beginning.
346
 
         */
347
 
        public synchronized void reset() {
348
 
                Bindings.rhash_reset(context_ptr);
349
 
                finished = false;
350
 
        }
351
 
 
352
 
        /**
353
 
         * Tests whether this <code>RHash</code> is finished or not.
354
 
         * @return
355
 
         *   <code>false</code> if this <code>RHash</code> is ready to
356
 
         *   receive new data for hashing;
357
 
         *   <code>true</code> if hash calculations are finished
358
 
         */
359
 
        public boolean isFinished() {
360
 
                return finished;
361
 
        }
362
 
 
363
 
        /**
364
 
         * Returns digest for given hash type.
365
 
         * 
366
 
         * @param type  hash type
367
 
         * @return  <code>Digest</code> for processed data
368
 
         * @throws NullPointerException
369
 
         *   if <code>type</code> is <code>null</code>
370
 
         * @throws IllegalStateException
371
 
         *   if this <code>RHash</code> is not finished
372
 
         * @throws IllegalArgumentException
373
 
         *   if this <code>RHash</code> was not created to calculate
374
 
         *   hash for specified algorithm
375
 
         */
376
 
        public Digest getDigest(HashType type) {
377
 
                if (type == null) {
378
 
                        throw new NullPointerException();
379
 
                }
380
 
                if (!finished) {
381
 
                        throw new IllegalStateException(ERR_UNFINISHED);
382
 
                }
383
 
                if ((hash_flags & type.hashId()) == 0) {
384
 
                        throw new IllegalArgumentException(ERR_WRONGTYPE+type);
385
 
                }
386
 
                return new Digest(Bindings.rhash_print(context_ptr, type.hashId()), type);
387
 
        }
388
 
 
389
 
        /**
390
 
         * Returns digest for processed data.
391
 
         * If more than one hashing type was passed to the
392
 
         * <code>RHash</code> constructor, then the least
393
 
         * hash type (in the order induced by
394
 
         * {@link Enum#compareTo(Enum) compareTo()}) is used.
395
 
         * 
396
 
         * @return <code>Digest</code> for processed data
397
 
         * @throws IllegalStateException
398
 
         *   if this <code>RHash</code> is not finished
399
 
         */
400
 
        public Digest getDigest() {
401
 
                return getDigest(deftype);
402
 
        }
403
 
        
404
 
        /**
405
 
         * Returns magnet link that includes specified filename
406
 
         * and hashes for given algorithms. Only hashes that were
407
 
         * computed by this <code>RHash</code> are included.
408
 
         *
409
 
         * @param  filename  file name to include in magnet, may be <code>null</code>
410
 
         * @return magnet link
411
 
         * @throws IllegalStateException
412
 
         *   if this <code>RHash</code> is not finished
413
 
         */
414
 
        public String getMagnet(String filename, HashType... types) {
415
 
                if (!finished) {
416
 
                        throw new IllegalStateException(ERR_UNFINISHED);
417
 
                }
418
 
                int flags = 0;
419
 
                for (HashType t : types) {
420
 
                        flags |= t.hashId();
421
 
                }
422
 
                return Bindings.rhash_print_magnet(context_ptr, filename, flags);
423
 
        }
424
 
 
425
 
        /**
426
 
         * Returns magnet link for given filename.
427
 
         * Magnet includes all hashes that were computed
428
 
         * by this <code>RHash</code>.
429
 
         * 
430
 
         * @param  filename  file name to include in magnet, may be <code>null</code>
431
 
         * @return magnet link
432
 
         * @throws IllegalStateException
433
 
         *   if this <code>RHash</code> is not finished
434
 
         */
435
 
        public String getMagnet(String filename) {
436
 
                if (!finished) {
437
 
                        throw new IllegalStateException(ERR_UNFINISHED);
438
 
                }
439
 
                return Bindings.rhash_print_magnet(context_ptr, filename, hash_flags);
440
 
        }
441
 
        
442
 
        /**
443
 
         * Called by garbage collector to free native resources.
444
 
         */
445
 
        @Override
446
 
        protected void finalize() {
447
 
                Bindings.rhash_free(context_ptr);
448
 
        }
449
 
}