001: /**
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */package com.tc.util;
004:
005: import com.tc.exception.TCRuntimeException;
006:
007: import java.io.IOException;
008: import java.io.StringWriter;
009: import java.io.Writer;
010:
011: /**
012: * Knows how to dump out byte arrays in the style of emacs' hexl-mode.
013: */
014: public class HexDump {
015:
016: private static final int BYTES_PER_LINE = 16;
017:
018: public static String dump(byte[] data) {
019: return dump(data, 0, data.length);
020: }
021:
022: public static String dump(byte[] data, int offset, int length) {
023: try {
024: StringWriter writer = new StringWriter();
025: dump(data, offset, length, writer);
026: return writer.toString();
027: } catch (IOException ioe) {
028: throw new TCRuntimeException(
029: "How'd we get an IOException with an in-memory stream?",
030: ioe);
031: }
032: }
033:
034: public static void dump(byte[] data, Writer out) throws IOException {
035: dump(data, 0, data.length, out);
036: }
037:
038: public static void dump(byte[] data, int offset, int length,
039: Writer out) throws IOException {
040: Assert.assertNotNull(data);
041: Assert.assertNotNull(out);
042: Assert.eval(offset >= 0);
043: Assert.eval(offset + length <= data.length);
044: Assert.eval(length >= 0);
045:
046: boolean multiline = length > BYTES_PER_LINE;
047: byte[] this Line = new byte[BYTES_PER_LINE];
048:
049: out.write(length + " byte" + (length == 1 ? "" : "s") + ":");
050: if (multiline)
051: out.write("\n");
052: else
053: out.write(" ");
054:
055: int linePos = 0;
056: for (int i = 0; i < length; ++i) {
057: if (i % BYTES_PER_LINE == 0 && multiline) {
058: if (i != 0)
059: out.write("\n");
060: out.write(padHex(i, 8) + ":");
061: linePos = 0;
062: }
063:
064: byte b = data[offset + i];
065: this Line[linePos++] = b;
066:
067: if (i % 2 == 0 && (multiline || i != 0))
068: out.write(" ");
069: out.write(padHex((b & 0x000000FF), 2));
070:
071: if (i == (length - 1)) {
072: int onLastLine = length
073: - ((length / BYTES_PER_LINE) * BYTES_PER_LINE);
074: if (onLastLine == 0)
075: onLastLine = BYTES_PER_LINE;
076: int remaining = BYTES_PER_LINE - onLastLine;
077:
078: if (multiline) {
079: if (remaining == 0)
080: out.write(" ");
081: if (remaining % 2 != 0) {
082: out.write(" ");
083: remaining--;
084: }
085:
086: Assert.eval(remaining % 2 == 0);
087:
088: while (remaining > 0) {
089: out.write(" ");
090: remaining -= 2;
091: }
092:
093: out.write(" ");
094: } else {
095: out.write(" ");
096: }
097:
098: for (int j = 0; j < onLastLine; ++j) {
099: out.write(getChar(this Line[j]));
100: }
101:
102: if (multiline)
103: out.write("\n");
104: } else if ((i + 1) % BYTES_PER_LINE == 0 && i != 0) {
105: out.write(" ");
106: for (int j = 0; j < this Line.length; ++j) {
107: out.write(getChar(this Line[j]));
108: }
109: }
110: }
111: }
112:
113: /**
114: * @return a "raw" hex dump (2 hex characters per byte), not in emacs' hexl mode.
115: */
116: public static String rawHexDump(byte[] data) {
117: StringBuffer hexDump = new StringBuffer();
118: for (int pos = 0; pos < data.length; ++pos)
119: hexDump.append(padHex(0xff & data[pos], 2));
120: return hexDump.toString();
121: }
122:
123: private static char getChar(byte b) {
124: int val = b & 0x000000FF;
125: if (val < 0x20 || val > 0x7E)
126: return '.';
127: else
128: return (char) b;
129: }
130:
131: private static String padHex(int num, int totalLength) {
132: String out = Integer.toHexString(num);
133: while (out.length() < totalLength) {
134: out = "0" + out;
135: }
136: return out;
137: }
138:
139: }
|