/******************************************************************************* * $Id$ * $Author$ * $Date$ * * Copyright 2002-2003 YAJUL Developers, Joshua Davis, Kent Vogel. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ******************************************************************************/ package org.yajul.io; import org.yajul.util.Bytes; import java.io.*; /** * An output stream that prints lines of hexadecimal output containing the byte * offset (in hex), the hex representation of the types, and the ASCII representation * of the bytes. * User: josh * Date: Jan 11, 2004 * Time: 9:23:13 PM */ public class HexDumpOutputStream extends HexEncodingOutputStream { /** * The width of each line (# of bytes). * */ private int width; /** * The nubmer of bytes on the current line. * */ private int count; /** * The total number of bytes. * */ private int total; /** * A buffer of the current bytes. * */ private byte[] buf; /** * Another buffer, used in converting the stream position into hex. * */ private byte[] intBytes; private final byte[] SEPARATOR = " | ".getBytes(); private static final char NON_PRINTABLE = '.'; private static final char NEWLINE = '\n'; /** * Returns a hex representation of the buffer. * * @param buf The buffer. * @param length The length * @return String - a hex representation of the buffer. */ public static String toHexString(byte[] buf, int length) { ByteArrayInputStream bais = new ByteArrayInputStream(buf); ByteArrayOutputStream baos = new ByteArrayOutputStream(); HexDumpOutputStream out = new HexDumpOutputStream(baos, 16); try { StreamCopier.unsyncCopy(bais, out, StreamCopier.DEFAULT_BUFFER_SIZE, length); out.flush(); } catch (IOException ignore) { // ignore } return new String(baos.toByteArray()); } /** * Creates an output stream filter built on top of the specified * underlying output stream. * * @param out the underlying output stream to be assigned to * the field <tt>this.out</tt> for later use, or * <code>null</code> if this instance is to be * created without an underlying stream. * @param width The number of bytes to print in a line of output. */ public HexDumpOutputStream(OutputStream out, int width) { super(out); this.width = width; this.count = 0; this.total = 0; this.buf = new byte[width]; // Create a buffer for the bytes. this.intBytes = new byte[4]; } /** * Writes the specified <code>byte</code> to this output stream. * <p/> * The <code>write</code> method of <code>FilterOutputStream</code> * calls the <code>write</code> method of its underlying output stream, * that is, it performs <tt>out.write(b)</tt>. * <p/> * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>. * * @param b the <code>byte</code>. * @throws IOException if an I/O error occurs. */ public void write(int b) throws IOException { // If the width has been reached, finish the current line and // start a new one. if (count == width) { writeASCII(); out.write(NEWLINE); count = 0; } // If this is the beginning of a line, write the total bytes // as a hex string. if (count == 0) { writeTotal(); out.write(SEPARATOR); } buf[count] = (byte) (0x000000FF & b); writeHex(b); count++; total++; } /** * Flushes this output stream and forces any buffered output bytes * to be written out to the stream. * <p/> * The <code>flush</code> method of <code>FilterOutputStream</code> * calls the <code>flush</code> method of its underlying output stream. * * @throws IOException if an I/O error occurs. * @see FilterOutputStream#out */ public void flush() throws IOException { // If there are bytes on the line. if (count > 0) { // Pad out to the first 'ascii' column. int pad = 2 * (width - count); for (int i = 0; i < pad; i++) out.write(' '); // Write the separator and the remaining bytes as ascii. writeASCII(); out.write(NEWLINE); } // Write the total number of bytes. writeTotal(); super.flush(); } private void writeASCII() throws IOException { out.write(SEPARATOR); for (int i = 0; i < count; i++) { if (buf[i] >= 32 && buf[i] < 127) out.write(buf[i]); else out.write(NON_PRINTABLE); } } private void writeTotal() throws IOException { Bytes.toBytes(total, intBytes); // Convert the integer into bytes. // Re-use 'buf' to hold the hex representation. Bytes.hexBytes(Bytes.HEX_BYTES_LOWER, intBytes, buf, 4); out.write(buf, 0, 8); } }