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


// @(#)MD5OutputStream.java, 1.3, 11/16/96





package marimba.io;





import java.io.*;


import marimba.util.Checksum;





/**


 * An output stream class which computes an


 * MD5 Checksum as it is going along,


 *


 * @author	Arthur van Hoff


 * @version 	1.3, 11/16/96


 */


public class MD5OutputStream extends FilterOutputStream {


    int digest[];


    byte buf[];


    int total;


    int count;





    public MD5OutputStream(OutputStream out, int digest[], int total) {


	super(out);


	this.digest = digest;


	this.total = total;


	this.buf = new byte[64];


    }





    public MD5OutputStream(OutputStream out) {


	this(out, Checksum.MD5Digest(), 0);


    }





    public MD5OutputStream(String path) throws IOException {


	this(new FastOutputStream(path));


    }





    public MD5OutputStream(File file) throws IOException {


	this(new FastOutputStream(file));


    }





    public void write(int b) throws IOException {


	total += 1;


	buf[count++] = (byte)b;





	// Flush the buffer when it is full


	if (count == 64) {


	    Checksum.MD5Add(buf, 0, digest);


	    out.write(buf, 0, 64);


	    count = 0;


	}


    }





    public void write(byte b[]) throws IOException {


	write(b, 0, b.length);


    }





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


	total += len;





	// Fill the buffer entirely, if possible


	if ((count > 0) && (count + len >= 64)) {


	    int n = 64 - count;


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


	    len -= n;


	    off += n;


	    Checksum.MD5Add(buf, 0, digest);


	    out.write(buf, 0, 64);


	    count = 0;


	}





	// Add chunks of 64 bytes


	int start = off;


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


	    Checksum.MD5Add(b, off, digest);


	}





	// Write the bytes that are not going into the buffer


	if (start < off) {


	    out.write(b, start, off - start);


	}


	if (len > 0) {


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


	    count += len;


	}


    }





    public void close() throws IOException {


	out.write(buf, 0, count);


	out.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];


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


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


	    MD5OutputStream out = new MD5OutputStream("tmp");


	    while (true) {


		int n = in.read(buf);


		if (n < 0) {


		    break;


		}


		out.write(buf, 0, n);


	    }


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


	    System.out.println("CHECKSUM2: " + out.getChecksum());


	    in.close();


	    out.close();





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


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


	    fin.close();


	}


    }


}


