/** * Hexdump.java * * Dumps a binary file to a ascii, in hexadecimal. * Copyright (c) 1999-2003 W.Finlay McWalter and the Free Software Foundation * Licence: version 2 of the GNU General Public Licence * * * This is a special test version, which unlike its predecessors (it's a * variant of v5) is multithreaded - the main thread reads from the source * fileand enqueues data blocks which are then consumed and hexdumped by a * second thread. The theory was that, for sufficiently large files where * the input and output files are on different disks, this would yield some * speedup, as the write and read wouldn't block one another. * * Nice theory, but it doesn't hold in practice, as last for JDK1.4.1 on * windowXP(sp1). Clearly the IO subsystem isn't being held up by the * interleaving of reads and writes (I figure the large buffers implemented * in v3 already take care of this). So all we get is a moderate (a few * percentage) slowdown due to the overhead of multithreadedness and * synchronisation. Playing around with the various buffer sizes doesn't * seem to produce any positive results. I can't, on the face of it, * figure out any improvements that will make matters any faster. I belive * the LinkedList implementation that's used for the FIFO between the * threads is not synchronised, and we need the synchronisation around * the queue as a whole to ensure its integrity. * */ import java.io.*; import java.util.LinkedList; class DataBlock{ public byte[] data; public int dataLength; public boolean keepRunning; /** * Normal constructor */ public DataBlock(byte [] d, int len){ data = d; dataLength = len; keepRunning = true; } /** * A special constructor that makes a DataBlock which signals * the end of the data stream. This block has no payload. */ public DataBlock(){ keepRunning = false; } } class QueueHandler { private LinkedList queueList; public QueueHandler () { queueList = new LinkedList(); } public synchronized void addToQueue (DataBlock val) { queueList.addLast(val); notifyAll(); } public synchronized DataBlock removeFromQueue () { while(queueList.size()==0){ try { wait(); } catch (InterruptedException e) {} } DataBlock val = (DataBlock)queueList.removeFirst(); return val; } } class PrintThread extends Thread { /** * the executable body of the thread that hexdumps datablocks out * to stdout. */ public void run(){ boolean keepRunning = true; while(keepRunning){ DataBlock receivedBlock = Hexdump.blockQueue.removeFromQueue(); if(receivedBlock.keepRunning){ Hexdump.processBlock(receivedBlock); } else { keepRunning = false; } } } } public class Hexdump { private static final int BYTES_PER_LINE = 16; private static final int PADDING = 4; private static final int READ_BUFFER_SIZE = (1024*8); private static final int MAX_UNWRITTEN_LINE_COUNT = 100; private static int runningCount=0; private static StringBuffer outBuffer = new StringBuffer(); private static int unwrittenLineCount=0; static QueueHandler blockQueue; /** * returns a char corresponding to the hex digit of the * supplied integer. */ private static char charFromHexDigit(int digit){ if((digit >= 0) && (digit <= 9)) return (char)(digit + '0'); else return (char)(digit - 10 + 'a'); } /** * @param inLine - a byte array containing the data to dump * (only a line's worth beginning at offset is dumped) * @param offset - location in inLine at which to start * @param linelength - the actual number of characters in this line. * This will equal BYTES_PER_LINE for all lines * bar the last one, which may be shorter. */ private static void printLine (byte[] inLine, int offset, int lineLength){ // print running location count for(int d=0; d < 8; d++){ int digitValue = (int)(runningCount >> (4*(7-d)))%16; outBuffer.append (charFromHexDigit((digitValue>=0)?digitValue :(16+digitValue))); } outBuffer.append(" "); // print hexbytes for(int x=0; x=0)?by:(256+by); outBuffer.append(charFromHexDigit(i/16)); outBuffer.append(charFromHexDigit(i%16)); outBuffer.append(' '); } // print padding between hexbytes and ascii for(int x=0; x < PADDING + ((BYTES_PER_LINE-lineLength)*3); x++){ outBuffer.append(' '); } // print ascii for(int x=0; x= ' ') && (v < (char)0x7f)){ outBuffer.append(v); } else { outBuffer.append('.'); } } outBuffer.append('\n'); unwrittenLineCount ++; if(unwrittenLineCount >= MAX_UNWRITTEN_LINE_COUNT){ System.out.print(outBuffer); outBuffer.setLength(0); unwrittenLineCount=0; } } static void processBlock(DataBlock block) { int bytesToWrite=block.dataLength; int writePos=0; while(bytesToWrite >= BYTES_PER_LINE){ printLine(block.data, writePos, BYTES_PER_LINE); writePos+=BYTES_PER_LINE; bytesToWrite-=BYTES_PER_LINE; runningCount += BYTES_PER_LINE; } if(bytesToWrite > 0){ printLine(block.data, writePos, bytesToWrite); } System.out.print(outBuffer); outBuffer.setLength(0); } /** * hexdumps the contents of a given open input file to stdout */ private static void dumpFile(FileInputStream in){ try { blockQueue = new QueueHandler(); PrintThread pThread = new PrintThread(); pThread.setPriority(Thread.MIN_PRIORITY); pThread.start(); byte[] data; int len=0; do { data = new byte [READ_BUFFER_SIZE]; len = in.read(data); if(len > 0){ // send this block to the print thread blockQueue.addToQueue(new DataBlock(data,len)); } } while (len > 0); // send an end-terminator to the print thread blockQueue.addToQueue(new DataBlock()); } catch (IOException e){ System.out.println("error reading file"); } } public static final void main (String [] args){ if(args.length == 1){ try { FileInputStream inStream = new FileInputStream(args[0]); dumpFile(inStream); inStream.close(); } catch (FileNotFoundException e){ System.out.println("File \"" + args[0] + "\" not found"); } catch (IOException e){ System.out.println("error closing file"); } } else { System.out.println("usage:\n java Hexdump \n"); } } }