This is an automated email from the ASF dual-hosted git repository. elecharny pushed a commit to branch 2.0.X in repository https://gitbox.apache.org/repos/asf/mina.git
The following commit(s) were added to refs/heads/2.0.X by this push: new 68ae3f5 Applied the addition of a preety hex dumper for IoBuffer (DIRMINA-1133) 68ae3f5 is described below commit 68ae3f5559ce91bc93d9f87c6365615cc843d058 Author: emmanuel lecharny <elecha...@apache.org> AuthorDate: Tue Feb 8 15:29:25 2022 +0100 Applied the addition of a preety hex dumper for IoBuffer (DIRMINA-1133) --- .../apache/mina/core/buffer/AbstractIoBuffer.java | 16 -- .../java/org/apache/mina/core/buffer/IoBuffer.java | 39 +++- .../apache/mina/core/buffer/IoBufferHexDumper.java | 225 ++++++++++++++++----- .../apache/mina/core/buffer/IoBufferWrapper.java | 8 - .../mina/core/buffer/IoBufferHexDumperTest.java | 47 +++++ 5 files changed, 257 insertions(+), 78 deletions(-) diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java index aa46c83..5d97142 100644 --- a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java +++ b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java @@ -1577,22 +1577,6 @@ public abstract class AbstractIoBuffer extends IoBuffer { * {@inheritDoc} */ @Override - public String getHexDump() { - return this.getHexDump(Integer.MAX_VALUE); - } - - /** - * {@inheritDoc} - */ - @Override - public String getHexDump(int lengthLimit) { - return IoBufferHexDumper.getHexdump(this, lengthLimit); - } - - /** - * {@inheritDoc} - */ - @Override public String getString(CharsetDecoder decoder) throws CharacterCodingException { if (!hasRemaining()) { return ""; diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBuffer.java b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBuffer.java index 882ca9c..27c932f 100644 --- a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBuffer.java +++ b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBuffer.java @@ -1511,24 +1511,49 @@ public abstract class IoBuffer implements Comparable<IoBuffer> { * Returns hexdump of this buffer. The data and pointer are not changed as a * result of this method call. * - * @return hexidecimal representation of this buffer + * @return hexadecimal representation of this buffer */ - public abstract String getHexDump(); + public String getHexDump() { + return this.getHexDump(remaining(), false); + } + + /** + * Returns hexdump of this buffer. The data and pointer are not changed as a + * result of this method call. + * + * @param pretty Produces multi-line pretty hex dumps + * @return hexadecimal representation of this buffer + */ + public String getHexDump(boolean pretty) { + return getHexDump(remaining(), pretty); + } /** * Return hexdump of this buffer with limited length. * - * @param lengthLimit - * The maximum number of bytes to dump from the current buffer - * position. + * @param length The maximum number of bytes to dump from the current buffer + * position. * @return hexidecimal representation of this buffer */ - public abstract String getHexDump(int lengthLimit); + public String getHexDump(int length) { + return getHexDump(length, false); + } + /** + * Return hexdump of this buffer with limited length. + * + * @param length The maximum number of bytes to dump from the current buffer + * position. + * @return hexidecimal representation of this buffer + */ + public String getHexDump(int length, boolean pretty) { + return (pretty) ? IoBufferHexDumper.getPrettyHexDumpSlice(this, position(), Math.min(remaining(), length)) + : IoBufferHexDumper.getHexDumpSlice(this, position(), Math.min(remaining(), length)); + } + // ////////////////////////////// // String getters and putters // // ////////////////////////////// - /** * Reads a <code>NUL</code>-terminated string from this buffer using the * specified <code>decoder</code> and returns it. This method reads until diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferHexDumper.java b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferHexDumper.java index 4a96bcd..c9f3763 100644 --- a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferHexDumper.java +++ b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferHexDumper.java @@ -19,6 +19,8 @@ */ package org.apache.mina.core.buffer; +import java.io.UnsupportedEncodingException; + /** * Provides utility methods to dump an {@link IoBuffer} into a hex formatted * string. @@ -26,77 +28,206 @@ package org.apache.mina.core.buffer; * @author <a href="http://mina.apache.org">Apache MINA Project</a> */ class IoBufferHexDumper { + private static final char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F' }; /** - * The high digits lookup table. + * Dumps an {@link IoBuffer} to a hex formatted string. + * + * @param buf the buffer to dump + * @param offset the starting position to begin reading the hex dump + * @param length the number of bytes to dump + * @return a hex formatted string representation of the <i>in</i> + * {@link IoBuffer}. */ - private static final byte[] highDigits; + public static String getHexDumpSlice(IoBuffer buf, int offset, int length) { + if (buf == null) { + throw new IllegalArgumentException(); + } + + if (length < 0 || offset < 0 || offset + length > buf.limit()) { + throw new IndexOutOfBoundsException(); + } + + int pos = offset; + int items = Math.min(offset + length, offset + buf.limit()) - pos; + + if (items <= 0) { + return ""; + } + + int lim = pos + items; + + StringBuilder out = new StringBuilder((items * 3) + 6); + + for (;;) { + int byteValue = buf.get(pos++) & 0xFF; + out.append((char) hexDigit[(byteValue >> 4) & 0x0F]); + out.append((char) hexDigit[byteValue & 0xf]); + if (pos < lim) { + out.append(' '); + } else { + break; + } + } + + return out.toString(); + } + /** - * The low digits lookup table. + * Produces a verbose hex dump + * + * @param buf The buffer to pretty print + * @param offset initial position which to read bytes + * @param length number of bytes to display + * + * @return The formatted String representing the content between (offset) and + * (offset+count) */ - private static final byte[] lowDigits; + public static final String getPrettyHexDumpSlice(IoBuffer buf, int offset, int length) { + if (buf == null) { + throw new IllegalArgumentException(); + } + + if (length < 0 || offset < 0 || offset + length > buf.limit()) { + throw new IndexOutOfBoundsException(); + } + + final int len = Math.min(length, buf.limit() - offset); + final byte[] bytes = new byte[len]; + + int o = offset; + + for (int i = 0; i < len; i++) { + bytes[i] = buf.get(o++); + } + + final StringBuilder sb = new StringBuilder(); + + sb.append("Source "); + sb.append(buf); + sb.append(" showing index "); + sb.append(offset); + sb.append(" through "); + sb.append((offset + length)); + sb.append("\n"); + sb.append(toPrettyHexDump(bytes, 0, bytes.length)); + + return sb.toString(); + } /** - * Initialize lookup tables. + * Generates a hex dump with line numbers, hex, volumes, and ascii + * representation + * + * @param data source data to read for the hex dump + * + * @param pos index position to begin reading + * + * @param len number of bytes to read + * + * @return string hex dump */ - static { - final byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + public static final String toPrettyHexDump(byte[] data, int pos, int len) { + if (data == null) { + throw new IllegalArgumentException(); + } + + if (len < 0 || pos < 0 || pos + len > data.length) { + throw new IndexOutOfBoundsException(); + } - int i; - byte[] high = new byte[256]; - byte[] low = new byte[256]; + StringBuilder buffer = new StringBuilder(); - for (i = 0; i < 256; i++) { - high[i] = digits[i >>> 4]; - low[i] = digits[i & 0x0F]; + // Process every byte in the data. + for (int i = pos, c = 0, line = 16; i < len; i += line) { + buffer.append(String.format("%06d", Integer.valueOf(c)) + " "); + + buffer.append(toPrettyHexDumpLine(data, i, Math.min((pos + len) - i, line), 8, line)); + + if ((i + line) < len) { + buffer.append("\n"); + } + + c += line; } - highDigits = high; - lowDigits = low; + return buffer.toString(); + } /** - * Dumps an {@link IoBuffer} to a hex formatted string. - * - * @param in the buffer to dump - * @param lengthLimit the limit at which hex dumping will stop - * @return a hex formatted string representation of the <i>in</i> {@link IoBuffer}. + * Generates the hex dump line with hex values, columns, and ascii + * representation + * + * @param data source data to read for the hex dump + * + * @param pos index position to begin reading + * + * @param len number of bytes to read; this can be less than the <tt>line</tt> + * width + * + * @param col number of bytes in a column + * + * @param line line width in bytes which pads the output if <tt>len</tt> is less + * than <tt>line</tt> + * + * @return string hex dump */ - public static String getHexdump(IoBuffer in, int length) { - if (length < 0) { - throw new IllegalArgumentException("length: " + length + " must be non-negative number"); - } + private static final String toPrettyHexDumpLine(byte[] data, int pos, int len, int col, int line) { + if ((line % 2) != 0) { + throw new IllegalArgumentException("length must be multiple of 2"); + } + + StringBuilder buffer = new StringBuilder(); - int pos = in.position(); - int rem = in.limit() - pos; - int items = Math.min(rem, length); + for (int i = pos, t = Math.min(data.length - pos, len) + pos; i < t;) { + for (int x = 0; (x < col) && (i < t); i++, x++) { + buffer.append(toHex(data[i])); + buffer.append(" "); + } - if (items == 0) { - return ""; - } + buffer.append(" "); + } + + int cl = (line * 3) + (line / col); - int lim = pos + items; + if (buffer.length() != cl) // check if we need to pad the output + { + cl -= buffer.length(); - StringBuilder out = new StringBuilder((items * 3) + 6); + while (cl > 0) { + buffer.append(" "); + cl--; + } + } - /* first sequence to align the spaces */{ - int byteValue = in.get(pos++) & 0xFF; - out.append((char) highDigits[byteValue]); - out.append((char) lowDigits[byteValue]); - } + try { + String p = new String(data, pos, Math.min(data.length - pos, len), "Cp1252").replace("\r\n", "..") + .replace("\n", ".").replace("\\", "."); - /* loop remainder */for (; pos < lim;) { - out.append(' '); - int byteValue = in.get(pos++) & 0xFF; - out.append((char) highDigits[byteValue]); - out.append((char) lowDigits[byteValue]); - } + char[] ch = p.toCharArray(); + + for (int m = 0; m < ch.length; m++) { + if (ch[m] < 32) { + ch[m] = (char) 46; // add dots for whitespace chars + } + } + + buffer.append(ch); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return buffer.toString(); + } - if (items != rem) { - out.append("..."); - } + public static final String toHex(byte b) { + // Returns hex String representation of byte - return out.toString(); + char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] }; + + return new String(array); } } \ No newline at end of file diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferWrapper.java b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferWrapper.java index 7616af1..26f51a0 100644 --- a/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferWrapper.java +++ b/mina-core/src/main/java/org/apache/mina/core/buffer/IoBufferWrapper.java @@ -816,14 +816,6 @@ public class IoBufferWrapper extends IoBuffer { * {@inheritDoc} */ @Override - public String getHexDump() { - return buf.getHexDump(); - } - - /** - * {@inheritDoc} - */ - @Override public String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException { return buf.getString(fieldSize, decoder); } diff --git a/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferHexDumperTest.java b/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferHexDumperTest.java new file mode 100644 index 0000000..560a202 --- /dev/null +++ b/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferHexDumperTest.java @@ -0,0 +1,47 @@ +package org.apache.mina.core.buffer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class IoBufferHexDumperTest { + + @Test + public void checkHexDumpLength() { + IoBuffer buf = IoBuffer.allocate(5000); + + for (int i = 0; i < 20; i++) { + buf.putShort((short) 0xF0A0); + } + + buf.flip(); + + /* special case */ + assertEquals(0, buf.getHexDump(0).length()); + + /* no truncate needed */ + assertEquals(buf.limit() * 3 - 1, buf.getHexDump().length()); + assertEquals((Math.min(300, buf.limit()) * 3) - 1, buf.getHexDump(300).length()); + + /* must truncate */ + assertEquals((7 * 3) - 1, buf.getHexDump(7).length()); + assertEquals((10 * 3) - 1, buf.getHexDump(10).length()); + assertEquals((30 * 3) - 1, buf.getHexDump(30).length()); + + } + + @Test + public void checkPrettyHexDumpLength() { + IoBuffer buf = IoBuffer.allocate(5000); + + for (int i = 0; i < 20; i++) { + buf.putShort((short) 0xF0A0); + } + + buf.flip(); + + String[] dump = buf.getHexDump(50, true).split("\\n"); + + assertEquals(4, dump.length); + } +}