/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.core.util;
import java.io.*;
/**
* Utility class for formatting byte[] data in the nicely organized hex dump format. In a hex dump,
* each byte is represented as a two-digit hexadecimal number. Each row consists of 16 bytes separated
* by white space. Each row is also pre appended with the memory address and post appended with the ASCII
* text for bytes.
*/
public class HexDumper {
public static final int BYTE_PER_ROW = 16;
private PrintStream printStream;
private int currentLineLength;
private int currentByte;
private int offset;
private byte[] thisLine = new byte[BYTE_PER_ROW];
/**
* Dump the hex bytes for the given input.
*
* @param inputBytes bytes to format at a hex dump.
* @return hex dump formatted byte data.
*/
public String dump(byte[] inputBytes) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes);
try {
this.dump(inputStream, outputStream);
} catch (Exception e) {
throw new IllegalStateException("Could not read input stream. ");
}
return outputStream.toString();
}
/**
* Dump the hex bytes for the given input to the specified out stream.
*
* @param inputBytes bytes to convert to hex dump format.
* @param outputStream output bytes where hex dump is written to.
* @throws IOException
*/
public void dump(InputStream inputBytes, OutputStream outputStream) throws IOException {
byte[] lineBytes = new byte[BYTE_PER_ROW];
reset(outputStream);
int lineLength;
do {
lineLength = readFully(inputBytes, lineBytes);
if (lineLength == 0) {
break;
}
writeMemoryOffset(lineLength);
// write out the bytes.
for (int i = 0; i < lineLength; i++) {
writeHexBytes(lineBytes, i);
}
writeASCIISummary();
} while (lineLength >= BYTE_PER_ROW);
}
/**
* Reads a line of byte data, ~16 bytes.
*
* @param inputBytes input array > 16 bytes.
* @param lineBytes output data.
* @return length of bytes copied.
* @throws IOException
*/
private int readFully(InputStream inputBytes, byte[] lineBytes) throws IOException {
for (int i = 0; i < lineBytes.length; ++i) {
int j = inputBytes.read();
if (j == -1) {
return i;
}
lineBytes[i] = (byte) j;
}
return lineBytes.length;
}
private void reset(OutputStream var1) throws IOException {
offset = 0;
printStream = new PrintStream(var1);
}
/**
* Write the memory offset to the start of each line.
*
* @param offsetLength line length.
* @throws IOException
*/
protected void writeMemoryOffset(int offsetLength) throws IOException {
convertByteToHex(printStream, (byte) (offset >>> 8 & 255));
convertByteToHex(printStream, (byte) (offset & 255));
printStream.print(": ");
currentByte = 0;
currentLineLength = offsetLength;
}
/**
* Writes out the byte[] one byte at a time converted to hex.
*
* @param byteArray array of data
* @param length length of bytes of convert.
* @throws IOException
*/
private void writeHexBytes(byte[] byteArray, int length) throws IOException {
thisLine[this.currentByte] = byteArray[length];
convertByteToHex(this.printStream, byteArray[length]);
printStream.print(" ");
currentByte++;
if (currentByte == 8) {
printStream.print(" ");
}
}
/**
* Write ASCII data out to end of pritn stream.
*
* @throws IOException
*/
private void writeASCIISummary() throws IOException {
// add some column padding
if (currentLineLength < BYTE_PER_ROW) {
for (int i = currentLineLength; i < BYTE_PER_ROW; i++) {
printStream.print(" ");
if (i == 7) {
printStream.print(" ");
}
}
}
printStream.print(" ");
// write out he ASCII version of the bytes which may or may not having meaning to end user.
for (int i = 0; i < currentLineLength; i++) {
if (this.thisLine[i] >= 32 && this.thisLine[i] <= 122) {
printStream.write(this.thisLine[i]);
} else {
printStream.print(".");
}
}
printStream.println();
offset += currentLineLength;
}
/**
* Convert byteValue to a two char hex number.
*
* @param printStream output stream.
* @param byteValue byte value to convert.
*/
private void convertByteToHex(PrintStream printStream, byte byteValue) {
// convert the first 4 bites to hex
char hexChar = (char) (byteValue >> 4 & 15);
if (hexChar > 9) {
hexChar = (char) (hexChar - 10 + 65);
} else {
hexChar = (char) (hexChar + 48);
}
printStream.write(hexChar);
// covert next 4 bits to hex.
hexChar = (char) (byteValue & 15);
if (hexChar > 9) {
hexChar = (char) (hexChar - 10 + 65);
} else {
hexChar = (char) (hexChar + 48);
}
printStream.write(hexChar);
}
}