// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)MD5InputStream.java, 1.2, 11/14/96





package marimba.io;





import java.io.*;


import marimba.util.Checksum;





/**


 * An input stream which computes an MD5 checksum


 * as it is going along.


 *


 * @author	Arthur van Hoff


 * @version 	1.2, 11/14/96


 */


public class MD5InputStream extends FilterInputStream {


    int digest[];


    byte buf[];


    int total;


    int pos;


    int count;





    public MD5InputStream(InputStream in, int digest[]) {


	super(in);


	this.digest = digest;


	this.buf = new byte[64];


    }





    public MD5InputStream(InputStream in) {


	this(in, Checksum.MD5Digest());


    }





    public MD5InputStream(String path) throws IOException {


	this(new FastInputStream(path));


    }





    public MD5InputStream(File file) throws IOException {


	this(new FastInputStream(file));


    }





    public int read() throws IOException {


	if (pos < count)


	    return (buf[pos++] & 0xFF);


	return fill() ? (buf[pos++] & 0xFF) : -1;


    }





    public int read(byte b[]) throws IOException {


	return read(b, 0, b.length);


    }





    public int read(byte b[], int off, int len) throws IOException {


	// Return the remainder of the buffer


	if (pos < count) {


	    int n = count - pos;


	    System.arraycopy(buf, pos, b, off, n);


	    pos = count;


	    return n;


	}





	// Read as many bytes as necessary


	int n = in.read(b, off, len);


	if (n <= 0) {


	    return (len > 0) ? -1 : 0;


	}


	len = n;


	total += n;





	// Top off the buffer if necessary


	if ((count > 0) && (count < 64)) {


	    int m = Math.min(64 - count, n);


	    System.arraycopy(b, off, buf, count, m);


	    pos += m;


	    count += m;


	    if (count == 64) {


		Checksum.MD5Add(buf, 0, digest);


		count = pos = 0;


	    }


	    off += m;


	    len -= m;


	}





	// Compute full blocks of 64 bytes


	for (; len >= 64 ; off += 64, len -= 64) {


	    Checksum.MD5Add(b, off, digest);


	}





	// Put remainder back in the buffer


	System.arraycopy(b, off, buf, 0, pos = count = len);


	return n;


    }





    boolean fill() throws IOException {


	// Rewind to the beginning of the buffer


	if (pos == 64) {


	    pos = count = 0;


	}





	// Try to top off the buffer


	int n = in.read(buf, pos, 64 - pos);


	if (n <= 0) {


	    return false;


	}


	count += n;


	total += n;





	// Compute the checksum if we have 64 bytes


	if (count == 64) {


	    Checksum.MD5Add(buf, 0, digest);


	}


	return true;


    }





    public long skip(long n) throws IOException {


	throw new IOException("Can't skip MD5 Streams");


    }





    public int available() throws IOException {


	return (count - pos) + in.available();


    }





    public void close() throws IOException {


	in.close();


    }





    /**


     * Return the checksum of this stream. The checksum can be computed


     * only once.


     */


    public Checksum getChecksum() {


	return Checksum.MD5Finish(buf, 0, count, total, digest);


    }





    /**


     * Get the current digest for the stream.


     */


    public int[] getDigest() {


	return digest;


    }





    /**


     * For testing purposes only.


     */


    public static void main(String argv[]) throws IOException {


	byte buf[] = new byte[10*1024 - 1231];


	for (int i = 0 ; i < argv.length ; i++) {


	    MD5InputStream in = new MD5InputStream(argv[i]);


	    while (true) {


		int n = in.read(buf);


		if (n < 0) {


		    break;


		}


	    }


	    System.out.println("CHECKSUM1: " + in.getChecksum());


	    in.close();





	    FastInputStream fin = new FastInputStream(argv[i]);


	    System.out.println("CHECKSUM2: " + Checksum.MD5(fin));


	    fin.close();


	}


    }


}


