33import java .io .ByteArrayInputStream ;
44import java .io .IOException ;
55import java .io .InputStream ;
6+ import java .nio .ByteBuffer ;
7+ import java .nio .ByteOrder ;
68import java .security .MessageDigest ;
79import java .security .NoSuchAlgorithmException ;
810import java .util .Arrays ;
911
1012public class HashCondenser {
1113 public static final int DEFAULT_OUTPUT_SIZE = 512 *1024 ;
14+ private static final int LONG_SIZE = Long .SIZE /8 ;
1215 private final MessageDigest digest ;
1316 private final int outputSize ;
1417 private final int digestLength ;
@@ -36,7 +39,7 @@ private HashCondenser(MessageDigest md, int outputSize) {
3639 digestLength = md .getDigestLength ();
3740 if (digestLength ==0 ) throw new IllegalArgumentException ("could not determine message digest length (returned 0)" );
3841 if (this .outputSize < this .digestLength ) throw new IllegalArgumentException ("output size is less than message digest length" );
39- this .hashCount = this .outputSize / digestLength ;
42+ this .hashCount = ( this .outputSize - LONG_SIZE ) / digestLength ;
4043 }
4144
4245 /**
@@ -66,26 +69,34 @@ public byte[] compute(byte[] data) {
6669 * @throws IOException when source throws an IOException
6770 * @throws IllegalArgumentException if sourceSize happens not to be the same as source's size
6871 */
69- public byte [] compute (InputStream source , long sourceSize ) throws IOException , IllegalArgumentException {
72+ public byte [] compute (InputStream source , long sourceSize ) throws IOException , IllegalArgumentException {
7073 // create result buffer
7174 byte [] result = new byte [this .outputSize ];
7275 Arrays .fill (result , (byte ) 0x00 );
7376
74- if (sourceSize <= this .outputSize ) {
77+ if (sourceSize <= this .outputSize - LONG_SIZE ) {
7578 // copy source directly if short enough
79+
80+ // add file size + negative sign as direct mode indicator
81+ ByteBuffer .wrap (result ).order (ByteOrder .BIG_ENDIAN ).putLong (-sourceSize );
82+
7683 int readSourceSize = 0 ;
7784 int readLength ;
78- while ((readLength = source .read (result , readSourceSize , (int ) (sourceSize -readSourceSize ))) != -1 ) {
85+ while ((readLength = source .read (result , LONG_SIZE + readSourceSize , (int ) (sourceSize -readSourceSize ))) != -1 ) {
7986 readSourceSize += readLength ;
8087 if (readLength ==0 ) {
8188 if (source .read () != -1 ) readSourceSize ++;
8289 break ;
8390 }
8491 }
85- if (readSourceSize != sourceSize ) throw new IllegalArgumentException ("read not as many bytes as sourceSize originally provided. Maybe the resource changed?" );
92+ if (readSourceSize != sourceSize ) throw new IllegalArgumentException ("read not as many bytes as sourceSize originally provided. Maybe the resource changed?" );
93+
8694 } else {
8795 // otherwise, hash segments
8896
97+ // add file size + positive sign as compressed mode indicator
98+ ByteBuffer .wrap (result ).order (ByteOrder .BIG_ENDIAN ).putLong (sourceSize );
99+
89100 // calculate segment size (input size for each hash)
90101 // if necessary, start hashing 1 byte more which will later be dropped so we hash exactly sourceSize bytes
91102 long segmentSize = sourceSize / hashCount ;
@@ -101,7 +112,7 @@ public byte[] compute(InputStream source, long sourceSize) throws IOException, I
101112 long previouslyReadSegmentSize = 0 ;
102113 int readLength = 0 ;
103114 int segmentIndex = 0 ;
104- int resultIndex = 0 ;
115+ int resultIndex = LONG_SIZE ;
105116
106117 // read all input
107118 while ((readLength = source .read (buf )) != -1 ) {
0 commit comments