h u f f m a n algorithm

25
BitInputStream.java import java.io.*; /** * Reads bits-at-a-time where the number of bits is between 1 and 32. * Updated for version 2.0 to extend java.io.InputStream. This class * can be used together with <code>BitOutputStream</code> to facilitate * reading and writing data several bits-at-a-time. BitInputStream objects * that are constructed from a File support <code>reset()</code>. However, * if constructed from an <code>InputStream</code> an object cannot be reset. * <P> * Any exceptions generated are rethrown as <code>RuntimeException</code> objects * so client code does not have to catch or rethrow them. (Unless the extension * of <code>InputStream</code> requires throwing as another type of exception, e.g., * as with method <code>read</code>. * <P> * @author Owen Astrachan * @version 1.0, July 2000 * @version 2.0, October 2004 */ public class BitInputStream extends InputStream { private InputStream myInput; private int myBitCount; private int myBuffer; private File myFile; private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff, 0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff, 0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff }; private static final int BITS_PER_BYTE = 8; /** * Construct a bit-at-a-time input stream from a file whose * name is supplied. * @param filename is the name of the file that will be read. * @throws RuntimeException if filename cannot be opened. */ public BitInputStream(String filename) { this(new File(filename)); } /**

Upload: jade-danial

Post on 25-May-2015

1.560 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: H U F F M A N Algorithm

BitInputStream.java

import java.io.*;

/** * Reads bits-at-a-time where the number of bits is between 1 and 32. * Updated for version 2.0 to extend java.io.InputStream. This class * can be used together with <code>BitOutputStream</code> to facilitate * reading and writing data several bits-at-a-time. BitInputStream objects * that are constructed from a File support <code>reset()</code>. However, * if constructed from an <code>InputStream</code> an object cannot be reset. * <P> * Any exceptions generated are rethrown as <code>RuntimeException</code> objects * so client code does not have to catch or rethrow them. (Unless the extension * of <code>InputStream</code> requires throwing as another type of exception, e.g., * as with method <code>read</code>. * <P> * @author Owen Astrachan * @version 1.0, July 2000 * @version 2.0, October 2004 */

public class BitInputStream extends InputStream{ private InputStream myInput; private int myBitCount; private int myBuffer; private File myFile; private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff, 0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff, 0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff };

private static final int BITS_PER_BYTE = 8; /** * Construct a bit-at-a-time input stream from a file whose * name is supplied. * @param filename is the name of the file that will be read. * @throws RuntimeException if filename cannot be opened. */ public BitInputStream(String filename) { this(new File(filename)); } /** * Construct a bit-at-a-time input stream from <code>file</code>. * @param file is the File that is the source of the input * @throws RuntimeExceptoin if file cannot be opened. */ public BitInputStream(File file) { myFile = file; try { reset(); } catch (IOException e) { throw new RuntimeException("could not open file for reading bits "+e);

Page 2: H U F F M A N Algorithm

} } /** * Open a bit-at-a-time stream that reads from supplied InputStream. If this * constructor is used the BitInputStream is not reset-able. * @param in is the stream from which bits are read. */ public BitInputStream(InputStream in){ myInput = in; myFile = null; } /** * Return true if the stream has been initialized from a File and * is thus reset-able. If constructed from an InputStream it is not reset-able. * @return true if stream can be reset (it has been constructed appropriately from a File). */ public boolean markSupported(){ return myFile != null; }

/** * Reset stream to beginning. The implementation creates a new * stream. * @throws IOException if not reset-able (e.g., constructed from InputStream). */ public void reset() throws IOException { if (! markSupported()){ throw new IOException("not resettable"); } try{ close(); myInput = new BufferedInputStream(new FileInputStream(myFile)); } catch (FileNotFoundException fnf){ System.err.println("error opening " + myFile.getName() + " " + fnf); } myBuffer = myBitCount = 0; }

/** * Closes the input stream. * @throws RuntimeException if the close fails */ public void close() { try{ if (myInput != null) { myInput.close(); } } catch (java.io.IOException ioe){ throw new RuntimeException("error closing bit stream " + ioe); } }

Page 3: H U F F M A N Algorithm

/** * Returns the number of bits requested as rightmost bits in * returned value, returns -1 if not enough bits available to * satisfy the request. * * @param howManyBits is the number of bits to read and return * @return the value read, only rightmost <code>howManyBits</code> * are valid, returns -1 if not enough bits left */

public int readBits(int howManyBits) throws IOException { int retval = 0; if (myInput == null){ return -1; } while (howManyBits > myBitCount){ retval |= ( myBuffer << (howManyBits - myBitCount) ); howManyBits -= myBitCount; try{ if ( (myBuffer = myInput.read()) == -1) { return -1; } } catch (IOException ioe) { throw new IOException("bitreading trouble "+ioe); } myBitCount = BITS_PER_BYTE; }

if (howManyBits > 0){ retval |= myBuffer >> (myBitCount - howManyBits); myBuffer &= bmask[myBitCount - howManyBits]; myBitCount -= howManyBits; } return retval; }

/** * Required by classes extending InputStream, returns * the next byte from this stream as an int value. * @return the next byte from this stream */ public int read() throws IOException { return readBits(8); }}

BitOutputStream.java

/** * Write bits-at-a-time where the number of bits is between 1 and 32 * Client programs must call <code>flush</code> or * <code>close</code> when finished writing or not all bits will be written. * This class is intended to be used with <code>BitInputStream</code> to * facilitate reading and writing data in a bits-at-a-time manner. * <P> * Updated for version 2.0 to extend java.io.OutputStream * <P> * Any exceptions generated are rethrown as <code>RuntimeException</code> objects

Page 4: H U F F M A N Algorithm

* so client code does not have to catch or rethrow them. * <P> * @author Owen Astrachan * @version 1.0, July 2000 * @version 2.0, October 2004 */

import java.io.*;

public class BitOutputStream extends OutputStream{

private OutputStream myOutput; private int myBuffer; private int myBitsToGo; private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff, 0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff, 0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff };

private static final int BITS_PER_BYTE = 8;

/** * Required by OutputStream subclasses, write the low * 8-bits to the underlying outputstream */ public void write(int b) throws IOException { myOutput.write(b); } /** * Create a stream that writes-through to the <code>OutputStream</code> object * passed as a parameter. * @param out is the output stream to which bits are written */ public BitOutputStream(OutputStream out){ myOutput = out; initialize(); } private void initialize(){ myBuffer = 0; myBitsToGo = BITS_PER_BYTE; } /** * Construct a bit-at-a-time output stream with specified file * name. * @param filename is the name of the file being written * @throws RuntimeException if opening file fails for either FileNotFound * or for Security exceptoins */ public BitOutputStream(String filename) { try{ myOutput = new BufferedOutputStream(new FileOutputStream(filename)); }

Page 5: H U F F M A N Algorithm

catch (FileNotFoundException fnf){ throw new RuntimeException("could not create " + filename + " " + fnf); } catch(SecurityException se){ throw new RuntimeException("security exception on write " + se); } initialize(); }

/** * Flushes bits not yet written, must be called by client * programs if <code>close</code> isn't called. * @throws RuntimeException if there's a problem writing bits */ public void flush() { if (myBitsToGo != BITS_PER_BYTE) { try{ write( (myBuffer << myBitsToGo) ); } catch (java.io.IOException ioe){ throw new RuntimeException("error writing bits on flush " + ioe); } myBuffer = 0; myBitsToGo = BITS_PER_BYTE; } try{ myOutput.flush(); } catch (java.io.IOException ioe){ throw new RuntimeException("error on flush " + ioe); } }

/** * Releases system resources associated with file and * flushes bits not yet written. Either this function * or flush must be called or not all bits will be written * @throws RuntimeException if close fails */ public void close() { flush(); try{ myOutput.close(); } catch (IOException ioe){ throw new RuntimeException("error closing BitOutputStream " + ioe); } }

/** * Write specified number of bits from value to a file. * @param howManyBits is number of bits to write (1-32) * @param value is source of bits, rightmost bits are written * @throws RuntimeException if there's an I/O problem writing bits */ public void writeBits(int howManyBits, int value)

Page 6: H U F F M A N Algorithm

{ value &= bmask[howManyBits]; // only right most bits valid

while (howManyBits >= myBitsToGo){ myBuffer = (myBuffer << myBitsToGo) | (value >> (howManyBits - myBitsToGo)); try{ write(myBuffer); } catch (java.io.IOException ioe){ throw new RuntimeException("error writing bits " + ioe); }

value &= bmask[howManyBits - myBitsToGo]; howManyBits -= myBitsToGo; myBitsToGo = BITS_PER_BYTE; myBuffer = 0; } if (howManyBits > 0) { myBuffer = (myBuffer << howManyBits) | value; myBitsToGo -= howManyBits; } }}

Diff.java

import java.io.*;

import javax.swing.JFileChooser;import javax.swing.JOptionPane;import javax.swing.ProgressMonitorInputStream;

public class Diff{ protected static JFileChooser ourChooser = new JFileChooser("."); public Diff(){ } public static void showMessage(String message){ JOptionPane.showMessageDialog(null, message,"Diff Output", JOptionPane.INFORMATION_MESSAGE); } public static boolean doDiffer(File[] files){ try { ProgressMonitorInputStream stream1 = new ProgressMonitorInputStream(

Page 7: H U F F M A N Algorithm

null, "reading "+files[0].getName(), new FileInputStream(files[0])); ProgressMonitorInputStream stream2 = new ProgressMonitorInputStream( null, "reading "+files[1].getName(), new FileInputStream(files[1])); BitInputStream b1 = new BitInputStream(stream1); BitInputStream b2 = new BitInputStream(stream2); while (true) { int x = b1.readBits(8); int y = b2.readBits(8); if (x == -1) return y == -1; if (y == -1) return false; if (x != y) return false; } // never reached } catch (IOException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null,"trouble reading","Diff Error", JOptionPane.ERROR_MESSAGE); return false; } } public static void main(String[] args){ ourChooser.setMultiSelectionEnabled(true); ourChooser.setDialogTitle("Diff: choose two files"); int retval = ourChooser.showOpenDialog(null); if (retval == JFileChooser.APPROVE_OPTION){ File[] files = ourChooser.getSelectedFiles(); if (files.length != 2){ JOptionPane.showMessageDialog(null,"Choose Two Files", "Diff Error",JOptionPane.ERROR_MESSAGE); } else { if (doDiffer(files)){ showMessage("Files are the same"); } else { showMessage("Files DIFFER somewhere"); } } } System.exit(0); }}

Huff.java

/** * Main/launch program for Huff assignment. A better * comment than this is warranted. * @author YOU THE STUDENT * */public class Huff {

public static void main(String[] args){

Page 8: H U F F M A N Algorithm

HuffViewer sv = new HuffViewer("Duke Compsci Huffing"); IHuffProcessor proc = new SimpleHuffProcessor(); sv.setModel(proc); }}

HuffMark.java

import javax.swing.JFileChooser;import javax.swing.JOptionPane;

import java.io.*;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;

public class HuffMark { protected static JFileChooser ourOpenChooser = new JFileChooser(System .getProperties().getProperty("user.dir")); static { ourOpenChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); } private double myTotalCompressTime; private long myTotalUncompressedBytes; private long myTotalCompressedBytes; private IHuffProcessor myHuffer; private static String SUFFIX = ".hf"; private static boolean FAST_READER = true; public void compress(File f) throws IOException{ if (f.getName().endsWith(SUFFIX)) return; // don't read .hf files! if (f.isDirectory()) return; // don't read directories double start = System.currentTimeMillis();

Page 9: H U F F M A N Algorithm

myHuffer.preprocessCompress(getFastByteReader(f)); File outFile = new File(getCompressedName(f)); FileOutputStream out = new FileOutputStream(outFile); System.out.println("compressing to: "+outFile.getCanonicalPath()); myHuffer.compress(getFastByteReader(f), out,true); double end = System.currentTimeMillis(); double time = (end-start)/1000.0; myTotalUncompressedBytes += f.length(); myTotalCompressedBytes += outFile.length(); myTotalCompressTime += time; System.out.printf("%s from\t %d to\t %d in\t %.3f\n",f.getName(),f.length(),outFile.length(),time); } public void doMark() throws IOException{ if (myHuffer == null){ myHuffer = new SimpleHuffProcessor(); } int action = ourOpenChooser.showOpenDialog(null); if (action == JFileChooser.APPROVE_OPTION){ File dir = ourOpenChooser.getSelectedFile(); File[] list = dir.listFiles(); for(File f : list){ compress(f); } System.out.println("--------"); System.out.printf("total bytes read: %d\n",myTotalUncompressedBytes); System.out.printf("total compressed bytes %d\n", myTotalCompressedBytes); System.out.printf("total percent compression %.3f\n",100.0* (1.0 - 1.0*myTotalCompressedBytes/myTotalUncompressedBytes)); System.out.printf("compression time: %.3f\n",myTotalCompressTime); } } public static void main(String[] args) throws IOException{ HuffMark hf = new HuffMark(); hf.doMark(); } private String getCompressedName(File f){ String name = f.getName(); String path = null; try { path = f.getCanonicalPath(); } catch (IOException e) { System.err.println("trouble with file canonicalizing "+f); return null; } int pos = path.lastIndexOf(name); String newName = path.substring(0, pos) + name + SUFFIX; return newName; } private InputStream getFastByteReader(File f) throws FileNotFoundException{ if (!FAST_READER){

Page 10: H U F F M A N Algorithm

return new FileInputStream(f); } ByteBuffer buffer = null; try { FileChannel channel = new FileInputStream(f).getChannel(); buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); byte[] barray = new byte[buffer.limit()]; if (barray.length != channel.size()){ System.err.println(String.format("Reading %s error: lengths differ %d %ld\n",f.getName(),barray.length,channel.size())); } buffer.get(barray); return new ByteArrayInputStream(barray); } catch (IOException e) { e.printStackTrace(); } return null; } }HuffViewer.java

import javax.swing.*;

import java.awt.BorderLayout;import java.awt.event.*;import java.util.*;import java.io.*;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;

/** * The GUI/View for Huffman coding assignment. Clients communicate * with this view by attaching a model and then using the menu choices/options that * are part of the GUI. Thus client code that fails to call <code>setModel</code> will * almost certainly not work and generate null pointer problems because the view/GUI will * not have an associated model. * <P> * @author Owen Astrachan * */public class HuffViewer extends JFrame { private static String HUFF_SUFFIX = ".hf"; private static String UNHUFF_SUFFIX = ".unhf"; private static boolean FAST_READER = true; protected JTextArea myOutput; protected IHuffProcessor myModel; protected String myTitle; protected JTextField myMessage; protected File myFile; private boolean myForce;

protected static JFileChooser ourChooser = new JFileChooser(System.getProperties().getProperty("user.dir"));

Page 11: H U F F M A N Algorithm

public HuffViewer(String title) { setDefaultCloseOperation(EXIT_ON_CLOSE);

JPanel panel = (JPanel) getContentPane(); panel.setLayout(new BorderLayout()); setTitle(title); myTitle = title; myForce = false;

panel.add(makeOutput(), BorderLayout.CENTER); panel.add(makeMessage(), BorderLayout.SOUTH); makeMenus();

pack(); setSize(400, 400); setVisible(true); }

/** * Associates this view with the given model. The GUI/View will * attach itself to the model so that communication between the view * and the model as well as <em>vice versa</em> is supported. * @param model is the model for this view */ public void setModel(IHuffProcessor model) { myModel = model; myModel.setViewer(this); }

protected JPanel makeMessage() { JPanel p = new JPanel(new BorderLayout()); myMessage = new JTextField(30); p.setBorder(BorderFactory.createTitledBorder("message")); p.add(myMessage, BorderLayout.CENTER); return p; }

protected JPanel makeOutput() { JPanel p = new JPanel(new BorderLayout()); myOutput = new JTextArea(10,40); p.setBorder(BorderFactory.createTitledBorder("output")); p.add(new JScrollPane(myOutput), BorderLayout.CENTER); return p;

}

protected File doRead() {

int retval = ourChooser.showOpenDialog(null); if (retval != JFileChooser.APPROVE_OPTION) { return null; } showMessage("reading/initializing"); myFile = ourChooser.getSelectedFile(); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(myFile),"counting/reading bits ..."); }

Page 12: H U F F M A N Algorithm

else { temp = getMonitorableStream(myFile,"counting/reading bits ..."); } final ProgressMonitorInputStream pmis = temp; final ProgressMonitor progress = pmis.getProgressMonitor(); try { Thread fileReaderThread = new Thread() { public void run() { try { int saved = myModel.preprocessCompress(pmis); HuffViewer.this.showMessage("saved: "+saved+" bits"); } catch (IOException e) { HuffViewer.this.showError("reading exception"); e.printStackTrace(); } if (progress.isCanceled()) { HuffViewer.this.showError("reading cancelled"); } } }; fileReaderThread.start(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } File ret = myFile; myFile = null; return ret; }

protected JMenu makeOptionsMenu() { JMenu menu = new JMenu("Options");

JCheckBoxMenuItem force = new JCheckBoxMenuItem(new AbstractAction( "Force Compression") { public void actionPerformed(ActionEvent ev) { myForce = !myForce; } }); menu.add(force); return menu;

}

protected JMenu makeFileMenu() { JMenu fileMenu = new JMenu("File");

fileMenu.add(new AbstractAction("Open/Count") { public void actionPerformed(ActionEvent ev) { doRead(); } });

fileMenu.add(new AbstractAction("Compress") { public void actionPerformed(ActionEvent ev) { doSave(); } });

Page 13: H U F F M A N Algorithm

fileMenu.add(new AbstractAction("Uncompress") { public void actionPerformed(ActionEvent ev) { doDecode(); } });

fileMenu.add(new AbstractAction("Quit") { public void actionPerformed(ActionEvent ev) { System.exit(0); } }); return fileMenu; }

protected void makeMenus() { JMenuBar bar = new JMenuBar(); bar.add(makeFileMenu()); bar.add(makeOptionsMenu()); setJMenuBar(bar); }

private void doDecode() { File file = null; showMessage("uncompressing"); try { int retval = ourChooser.showOpenDialog(null); if (retval != JFileChooser.APPROVE_OPTION) { return; } file = ourChooser.getSelectedFile(); String name = file.getName(); String uname = name; if (name.endsWith(HUFF_SUFFIX)) { uname = name.substring(0,name.length() - HUFF_SUFFIX.length()) + UNHUFF_SUFFIX; } else { uname = name + UNHUFF_SUFFIX; } String newName = JOptionPane.showInputDialog(this, "Name of uncompressed file", uname); if (newName == null) { return; } String path = file.getCanonicalPath();

int pos = path.lastIndexOf(name); newName = path.substring(0, pos) + newName; final File newFile = new File(newName); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(file),"uncompressing bits ..."); } else { temp = getMonitorableStream(file, "uncompressing bits..."); } final ProgressMonitorInputStream stream = temp; final ProgressMonitor progress = stream.getProgressMonitor(); final OutputStream out = new FileOutputStream(newFile); Thread fileReaderThread = new Thread() {

Page 14: H U F F M A N Algorithm

public void run() { try { myModel.uncompress(stream, out); } catch (IOException e) { HuffViewer.this.showError("reading exception"); e.printStackTrace(); } if (progress.isCanceled()) { HuffViewer.this.showError("reading cancelled"); } } }; fileReaderThread.start(); } catch (FileNotFoundException e) { showError("could not open " + file.getName()); e.printStackTrace(); } catch (IOException e) { showError("IOException, uncompression halted from viewer"); e.printStackTrace(); } }

private void doSave() { myFile = doRead(); if (myFile == null){ return; } String name = myFile.getName(); showMessage("compressing "+name); String newName = JOptionPane.showInputDialog(this, "Name of compressed file", name + HUFF_SUFFIX); if (newName == null) { return; } String path = null; try { path = myFile.getCanonicalPath(); } catch (IOException e) { showError("trouble with file canonicalizing"); return; } int pos = path.lastIndexOf(name); newName = path.substring(0, pos) + newName; final File file = new File(newName); try { final FileOutputStream out = new FileOutputStream(file); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(myFile),"compressing bits..."); } else { temp = getMonitorableStream(myFile,"compressing bits ..."); } final ProgressMonitorInputStream pmis = temp; final ProgressMonitor progress = pmis.getProgressMonitor(); Thread fileWriterThread = new Thread() { public void run() { try { myModel.compress(pmis, out,myForce); } catch (IOException e) { HuffViewer.this.showError("compression exception");

Page 15: H U F F M A N Algorithm

e.printStackTrace(); } if (progress.isCanceled()) { HuffViewer.this.showError("compression cancelled"); cleanUp(file); } } }; fileWriterThread.start(); } catch (FileNotFoundException e) { showError("could not open " + file.getName()); e.printStackTrace(); } myFile = null; }

private void cleanUp(File f) { if (!f.delete()) { showError("trouble deleting " + f.getName()); } else { // do something here? } }

private ProgressMonitorInputStream getMonitorableStream(InputStream stream, String message) {

final ProgressMonitorInputStream pmis = new ProgressMonitorInputStream( this, message, stream);

ProgressMonitor progress = pmis.getProgressMonitor(); progress.setMillisToDecideToPopup(1); progress.setMillisToPopup(1);

return pmis; } private ProgressMonitorInputStream getMonitorableStream(File file, String message) { try { FileInputStream stream = new FileInputStream(file); if (stream == null){ System.out.println("null on "+file.getCanonicalPath()); } final ProgressMonitorInputStream pmis = new ProgressMonitorInputStream( this, message, stream);

ProgressMonitor progress = pmis.getProgressMonitor(); progress.setMillisToDecideToPopup(1); progress.setMillisToPopup(1);

return pmis; } catch (IOException e) { showError("could not open " + file.getName()); e.printStackTrace(); return null; } }

Page 16: H U F F M A N Algorithm

/** * To be called by model/client code to display strings in the GUI. Each object * in parameter elements will be displayed as a string in this view. * @param elements is source of objects that will be displayed, each object's * <code>toString</code> method wil be called to display. */ public void update(Collection elements) { showMessage(""); myOutput.setText(""); for(Object o : elements){ myOutput.append(o+"\n"); } }

/** * Display a text message in the view (e.g., in the small text area * at the bottom of the GUI), thus a modeless message the user can ignore. * @param s is the message displayed */ public void showMessage(String s) { myMessage.setText(s); }

/** * Show a modal-dialog indicating an error; the user must dismiss the * displayed dialog. * @param s is the error-message displayed */ public void showError(String s) { JOptionPane.showMessageDialog(this, s, "Huff info", JOptionPane.INFORMATION_MESSAGE); } private ByteArrayInputStream getFastByteReader(File f){ ByteBuffer buffer = null; try { FileChannel channel = new FileInputStream(f).getChannel(); buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); byte[] barray = new byte[buffer.limit()]; if (barray.length != channel.size()){ showError(String.format("Reading %s error: lengths differ %d %ld\n",f.getName(),barray.length,channel.size())); } buffer.get(barray); return new ByteArrayInputStream(barray); } catch (IOException e) { e.printStackTrace(); } return null; } }

IHuffConstants.java

/**

Page 17: H U F F M A N Algorithm

* Global constants used in Huff/Unhuff. Clients needing these * values should implement this interface or access the * values directly, e.g., as <code>IHuffConstants.BITS_PER_WORD</code>. However, * implementing the interface is preferred in which case * the values can be accessed simply as <code>BITS_PER_WORD</code>, for example. * <P> * @author Owen Astrachan */public interface IHuffConstants { /** * The standard number of bits per chunk/word when huffing. */ public static final int BITS_PER_WORD = 8; /** * The size of the alphabet given the number of bits per chunk, this * should be 2^BITS_PER_WORD. */ public static final int ALPH_SIZE = (1 << BITS_PER_WORD); /** * The standard number of bits needed to represent/store * an int, this is 32 in Java and nearly all other languages. */ public static final int BITS_PER_INT = 32; /** * The value of the PSEUDO_EOF character. This is one-more * than the maximum value of a legal BITS_PER_WORD-bit character. */ public static final int PSEUDO_EOF = ALPH_SIZE; /** * Isolate the magic number in one place. */ public static final int MAGIC_NUMBER = 1234567873;}

IHuffProcessor.java

/** * The interface for the model that can be attached * to a HuffViewer. Most of the work done in huffing * (and unhuffing) will be via a class that implements

Page 18: H U F F M A N Algorithm

* this interface. The interface may need to be extended * depending on the design of the huffman program. * <P> * @author Owen Astrachan * */

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.File;

public interface IHuffProcessor extends IHuffConstants { /** * Make sure this model communicates with some view. * @param viewer is the view for communicating. */ public void setViewer(HuffViewer viewer); /** * Preprocess data so that compression is possible --- * count characters/create tree/store state so that * a subsequent call to compress will work. The InputStream * is <em>not</em> a BitInputStream, so wrap it int one as needed. * @param in is the stream which could be subsequently compressed * @return number of bits saved by compression or some other measure */ public int preprocessCompress(InputStream in) throws IOException;

/** * Compresses input to output, where the same InputStream has * previously been pre-processed via <code>preprocessCompress</code> * storing state used by this call. * @param in is the stream being compressed (not a BitInputStream) * @param out is bound to a file/stream to which bits are written * for the compressed file (not a BitOutputStream) * @return the number of bits written */

public int compress(InputStream in, OutputStream out, boolean force) throws IOException;

/** * Uncompress a previously compressed stream in, writing the * uncompressed bits/data to out. * @param in is the previously compressed data (not a BitInputStream) * @param out is the uncompressed file/stream * @return the number of bits written to the uncompressed file/stream */ public int uncompress(InputStream in, OutputStream out) throws IOException; }

Page 19: H U F F M A N Algorithm

SimpleHuffProcessor...>

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;

public class SimpleHuffProcessor implements IHuffProcessor { private HuffViewer myViewer; public int compress(InputStream in, OutputStream out, boolean force) throws IOException { myViewer.showError("compress is not implemented"); return 0; }

public int preprocessCompress(InputStream in) throws IOException { myViewer.showError("preprocessCompress is not implemented"); return 0; }

public void setViewer(HuffViewer viewer) { myViewer = viewer; }

public int uncompress(InputStream in, OutputStream out) throws IOException { myViewer.showError("uncompress is not implemented"); return 0; } private void showMessage(ArrayList<String> list){ myViewer.update(list); }

}

TreeNode.java

/** * Utility binary-tree (Huffman tree) node for Huffman coding. * This is a simple, standard binary-tree node implementing * the comparable interface based on weight. * * @author Owen Astrachan * @version 1.0 July 2000 * @version 2.0 Jan 2006 */public class TreeNode implements Comparable<TreeNode> { public int myValue; public int myWeight;

Page 20: H U F F M A N Algorithm

public TreeNode myLeft; public TreeNode myRight;

/** * construct leaf node (null children) * * @param value * is the value stored in the node (e.g., character) * @param weight * is used for comparison (e.g., count of # occurrences) */

public TreeNode(int value, int weight) { myValue = value; myWeight = weight; }

/** * construct internal node (with children) * * @param value * is stored as value of node * @param weight * is weight of node * @param ltree * is left subtree * @param rtree * is right subtree */

public TreeNode(int value, int weight, TreeNode ltree, TreeNode rtree) { this(value, weight); myLeft = ltree; myRight = rtree; }

/** * Return value based on comparing this TreeNode to another. * @return -1 if this < o, +1 if this > o, and 0 if this == 0 */

public int compareTo(TreeNode rhs) {

return myWeight - rhs.myWeight; }}

Page 21: H U F F M A N Algorithm