HexUtils.java
/*
* @copyright defined in LICENSE.txt
*/
package hera.util;
import static com.google.common.io.Closeables.close;
import static hera.util.ValidationUtils.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
public class HexUtils {
/* Dump Format */
protected static final char CONTROL_CHARS_SHOWER = '.';
protected static final char[] HEXA_CHARS =
new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
protected static final int N_INT_BY_BYTE = 4;
protected static final int WIDTH_PER_LINE = 32;
protected static final char TWO_BYTES_CHARS_SHOWER = '?';
/**
* Append hex value of {@code ch} to {@code buffer}.
*
* @param buffer buffer to append to
* @param ch value to append
*/
public static void appendHexa(final StringBuilder buffer, final int ch) {
if (ch < 16) {
buffer.append('0');
buffer.append(HEXA_CHARS[(0x0f & (ch))]);
} else {
buffer.append(HEXA_CHARS[(0x0f & (ch >> 4))]);
buffer.append(HEXA_CHARS[(0x0f & (ch))]);
}
}
/**
* Append hex values of {@code bytes} to {@code buffer}.
*
* @param buffer buffer to append to
* @param bytes values to append
*/
public static void appendHexa(final StringBuilder buffer, final byte[] bytes) {
try {
final ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
int ch = 0;
while (0 <= (ch = byteIn.read())) {
appendHexa(buffer, ch);
}
} catch (final Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Encode byte array to hexa.
*
* @param bytes byte array to encode
*
* @return encoded string
*/
public static String encode(final byte[] bytes) {
final StringBuilder buffer = new StringBuilder();
appendHexa(buffer, bytes);
return buffer.toString();
}
protected static int convert(final int ch) {
if ('0' <= ch && ch <= '9') {
return ch - '0';
} else if ('A' <= ch && ch <= 'F') {
return 10 + ch - 'A';
} else if ('a' <= ch && ch <= 'f') {
return 10 + ch - 'a';
} else {
throw new IllegalArgumentException();
}
}
/**
* Decode hex string to byte array.
*
* @param str hex string
*
* @return decoded byte array
*/
public static byte[] decode(final String str) {
final StringReader reader = new StringReader(str);
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
int ch1 = 0;
try {
while (0 < (ch1 = reader.read())) {
int ch2 = reader.read();
assertTrue(0 <= ch2);
byteOut.write(convert(ch1) << 4 | convert(ch2));
}
return byteOut.toByteArray();
} catch (final IOException e) {
throw new IllegalStateException(e);
}
}
protected static void lineEnd(final StringBuilder hexPart, final StringBuilder textPart,
final StringBuilder ret) {
hexPart.append(" |");
textPart.append("|\n");
ret.append(hexPart);
ret.append(textPart);
hexPart.delete(0, hexPart.capacity());
textPart.delete(0, textPart.capacity());
}
/**
* Convert {@code data} to readable dump for human.
*
* @param data byte array to convert
*
* @return converted string
*/
public static String dump(final byte[] data) {
if (null == data) {
return "<<null>>";
}
return dump(data, 0, data.length);
}
/**
* Convert {@code data}'s subsequence to readable dump for human.
*
* @param data byte array to convert
* @param offset sequence start index
* @param length sequence length
*
* @return converted string
*/
public static String dump(final byte[] data, final int offset, final int length) {
final StringWriter writer = new StringWriter();
dump(data, offset, length, writer);
return writer.toString();
}
/**
* Convert {@code data}'s subsequence to readable dump for human and write to {@code writer}.
*
* @param data byte array to convert
* @param offset sequence start index
* @param length sequence length
* @param writer writer to write result
*/
public static void dump(final byte[] data, final int offset, final int length,
final Writer writer) {
try {
if (null == data) {
writer.write("<<null>>");
return;
}
if (data.length <= 0) {
writer.write("<<EMPTY BYTES>>");
return;
}
final ByteArrayInputStream reader = new ByteArrayInputStream(data, offset, length);
final StringBuilder ret = new StringBuilder();
final StringBuilder hexPart = new StringBuilder();
final StringBuilder textPart = new StringBuilder();
int address = 0;
int cnt = 0;
hexPart.append(" ");
for (int i = 0, n = WIDTH_PER_LINE / 4; i < n; i++) {
hexPart.append("+-------");
textPart.append("+---");
}
lineEnd(hexPart, textPart, ret);
int ch;
while (0 <= (ch = reader.read())) {
if (0 == cnt) {
for (int i = N_INT_BY_BYTE - 1; i >= 0; i--) {
final int printByte = 0xFF & (address >> (8 * i));
appendHexa(hexPart, printByte);
}
hexPart.append(" ");
address += WIDTH_PER_LINE;
}
appendHexa(hexPart, ch);
if (ch < 32 || 127 <= ch) {
textPart.append(CONTROL_CHARS_SHOWER);
} else {
textPart.append((char) ch);
}
cnt++;
if (WIDTH_PER_LINE == cnt) {
lineEnd(hexPart, textPart, ret);
cnt = 0;
}
} // END of while ( 0 <= (ch = reader.read() ) )
if (0 != cnt) {
for (; cnt < WIDTH_PER_LINE; ++cnt) {
hexPart.append(" ");
textPart.append(' ');
}
lineEnd(hexPart, textPart, ret);
}
writer.write(ret.toString());
close(writer, true);
} catch (final IOException e) {
throw new IllegalStateException(e);
}
}
}