Skip to content
Permalink
Browse files
8247274: (test) HexPrinter cleanup
Reviewed-by: lancea
  • Loading branch information
Roger Riggs committed Jun 10, 2020
1 parent 9913602 commit 120a0d116a22dec1b89eea29ae85daeb348b4ac8
Showing 2 changed files with 132 additions and 64 deletions.
@@ -77,10 +77,9 @@ Object[][] columnParams() {
Object[][] builtinParams() {
return new Object[][]{
{"minimal", "", "%02x", 16, "", 64, HexPrinter.Formatters.NONE, ""},
{"canonical", "%08x ", "%02x ", 16, "|", 31, HexPrinter.Formatters.ASCII, "|\n"},
{"simple", "%5d: ", "%02x ", 16, " // ", 64, HexPrinter.Formatters.PRINTABLE, "\n"},
{"source", " ", "(byte)%3d, ", 8, " // ", 64, HexPrinter.Formatters.PRINTABLE,
"\n"},
{"canonical", "%08x ", "%02x ", 16, "|", 31, HexPrinter.Formatters.PRINTABLE, "|\n"},
{"simple", "%5d: ", "%02x ", 16, " // ", 64, HexPrinter.Formatters.ASCII, "\n"},
{"source", " ", "(byte)%3d, ", 8, " // ", 64, HexPrinter.Formatters.PRINTABLE, "\n"},
};
}

@@ -162,6 +161,52 @@ public static void testParams(HexPrinter printer, String offsetFormat, String bi
}
}

@Test
static void testPrintable() {
String expected =
"................................" +
" !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
"`abcdefghijklmnopqrstuvwxyz{|}~." +
"................................" +
"................................" +
"................................" +
"................................";
byte[] bytes = new byte[256];
for (int i = 0; i < bytes.length; i++)
bytes[i] = (byte)i;
HexPrinter p = HexPrinter.minimal()
.withBytesFormat("", 256)
.formatter(HexPrinter.Formatters.PRINTABLE, "", 512);
String actual = p.toString(bytes);
Assert.assertEquals(actual, expected, "Formatters.Printable mismatch");
}

@Test
static void testASCII() {
String expected = "\\nul\\soh\\stx\\etx\\eot\\enq\\ack\\bel\\b\\t\\n\\vt\\f\\r\\so\\si\\dle" +
"\\dc1\\dc2\\dc3\\dc4\\nak\\syn\\etb\\can\\em\\sub\\esc\\fs\\gs\\rs\\us" +
" !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
"`abcdefghijklmnopqrstuvwxyz{|}~\\127" +
"\\128\\129\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139\\140\\141\\142\\143" +
"\\144\\145\\146\\147\\148\\149\\150\\151\\152\\153\\154\\155\\156\\157\\158\\159" +
"\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169\\170\\171\\172\\173\\174\\175" +
"\\176\\177\\178\\179\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189\\190\\191" +
"\\192\\193\\194\\195\\196\\197\\198\\199\\200\\201\\202\\203\\204\\205\\206\\207" +
"\\208\\209\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219\\220\\221\\222\\223" +
"\\224\\225\\226\\227\\228\\229\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239" +
"\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249\\250\\251\\252\\253\\254\\255";
byte[] bytes = new byte[256];
for (int i = 0; i < bytes.length; i++)
bytes[i] = (byte)i;
HexPrinter p = HexPrinter.minimal()
.withBytesFormat("", 256)
.formatter(HexPrinter.Formatters.ASCII, "", 256);
String actual = p.toString(bytes);
Assert.assertEquals(actual, expected, "Formatters.ASCII mismatch");
}

@DataProvider(name = "PrimitiveFormatters")
Object[][] formatterParams() {
return new Object[][]{
@@ -252,6 +297,13 @@ Object[][] sources() {
};
}

@DataProvider(name = "badsources")
Object[][] badSources() {
return new Object[][]{
{genBytes(21), 5, 22},
};
}

public static byte[] genData(int len) {
// Create a byte array with data for two lines
byte[] bytes = new byte[len];
@@ -312,6 +364,26 @@ public void testNilPrinterBigBuffer() {
Assert.assertEquals(r.replace("00", "").length(), 0, "contents not all zeros");
}

@Test(dataProvider = "badsources",
expectedExceptions = java.lang.IndexOutOfBoundsException.class)
public void testBadToStringByteBuffer(byte[] bytes, int offset, int length) {
if (length < 0)
length = bytes.length - offset;
ByteBuffer bb = ByteBuffer.wrap(bytes, 0, bytes.length);
System.out.printf("Source: %s, off: %d, len: %d%n",
bytes.getClass().getName(), offset, length);
String actual;
if (offset == 0 && length < 0) {
bb.position(offset);
bb.limit(length);
actual = HexPrinter.simple().toString(bb);
} else
actual = HexPrinter.simple().toString(bb, offset, length);
System.out.println(actual);
String expected = HexPrinter.simple().toString(bytes, offset, length);
Assert.assertEquals(actual, expected, "mismatch in format()");
}

@Test(dataProvider = "sources")
public void testToStringByteBuffer(byte[] bytes, int offset, int length) {
if (length < 0)
@@ -91,7 +91,7 @@
* {@linkplain Formatter Formatter} functions read and interpret the bytes to show the
* structure and content of a protocol or data stream.
* Built-in formatters include {@link HexPrinter#formatter(Class, String) primitives},
* {@link Formatters#PRINTABLE printable ascii},
* {@link Formatters#PRINTABLE printable bytes},
* and {@link Formatters#utf8Parser(DataInputStream, Appendable) UTF-8 strings}.
* The {@link #formatter(Formatter, String, int) formatter} method sets the
* formatting function, the delimiter, and the width.
@@ -238,7 +238,7 @@ public static HexPrinter minimal() {
* <LI>each byte value is formatted as 2 hex digits and a space: {@code "%02x "},
* <LI>maximum number of byte values per line: {@value initBytesCount},
* <LI>delimiter for the annotation: {@code "|"},
* <LI>formatter: {@link Formatters#ASCII ASCII bytes}, and
* <LI>formatter: {@link Formatters#PRINTABLE printable bytes}, and
* <LI>line separator: "|" + {@link System#lineSeparator()},
* <LI>destination: {@link System#out System.out}.
* </UL>
@@ -254,7 +254,7 @@ public static HexPrinter minimal() {
* @return a new HexPrinter
*/
public static HexPrinter canonical() {
return new HexPrinter(Formatters.ASCII, "%08x ",
return new HexPrinter(Formatters.PRINTABLE, "%08x ",
"%02x ", initBytesCount,
"|", 31, "|" + System.lineSeparator(),
System.out);
@@ -271,7 +271,7 @@ public static HexPrinter canonical() {
* <LI>delimiter for the annotation: {@code " // "},
* <LI>width for the annotation: {@value initAnnoWidth},
* <LI>line separator: {@link System#lineSeparator()},
* <LI>formatter: {@link Formatters#PRINTABLE printable ASCII}
* <LI>formatter: {@link Formatters#ASCII ASCII bytes}
* showing printable characters, mnemonics for control chars, and
* otherwise the decimal byte values,
* <LI>destination default: {@link System#out System.out}.
@@ -288,7 +288,7 @@ public static HexPrinter canonical() {
* @return a new HexPrinter
*/
public static HexPrinter simple() {
return new HexPrinter(Formatters.PRINTABLE, initOffsetFormat,
return new HexPrinter(Formatters.ASCII, initOffsetFormat,
initBytesFormat, initBytesCount,
initAnnoDelim, initAnnoWidth, System.lineSeparator(),
System.out);
@@ -305,9 +305,8 @@ public static HexPrinter simple() {
* <LI>delimiter for the annotation: {@code " // "},
* <LI>width for the annotation: {@value initAnnoWidth},
* <LI>line separator: {@link System#lineSeparator()},
* <LI>formatter: {@link Formatters#PRINTABLE printable ASCII}
* showing printable characters, mnemonics for control chars, and
* otherwise the decimal byte values,
* <LI>formatter: {@link Formatters#PRINTABLE printable bytes}
* showing printable characters and otherwise ".",
* <LI>destination default: {@link System#out System.out}.
* </UL>
*
@@ -426,34 +425,30 @@ private HexPrinter format(InputStream source, int offset) {
}

/**
* The formatter function is called repeatedly to read the bytes
* from the offset for the length and append the output.
* The formatter function is called for the range of the ByteBuffer's contents.
* All annotation output is appended and flushed to the output destination.
* The ByteBuffer position and limit are unused and not modified.
* The ByteBuffer position is not used and not modified.
*
* @param source a ByteBuffer
* @param offset the offset in the ByteBuffer
* @param length the length in the ByteBuffer
* @param index the index in the ByteBuffer, must be non-negative and
* less than {@code limit()}.
* @param length the length in the ByteBuffer must be non-negative and
* no larger than {@code source.limit() - index}
* @return this HexPrinter
* @throws java.io.UncheckedIOException if an I/O error occurs
* @throws java.lang.IndexOutOfBoundsException if the preconditions on
* {@code index} and {@code length} do not hold
*/
public HexPrinter format(ByteBuffer source, int offset, int length) {
public HexPrinter format(ByteBuffer source, int index, int length) {
Objects.requireNonNull(source, "ByteBuffer must be non-null");
ByteArrayInputStream bais;
if (source.hasArray() && !source.isReadOnly()) {
bais = new ByteArrayInputStream(source.array(), offset, length);
} else {
int size = source.limit() - source.position();
byte[] bytes = new byte[size];
source.get(bytes, offset, length);
bais = new ByteArrayInputStream(bytes);
}
return format(bais, offset);
byte[] bytes = new byte[length];
source.get(index, bytes, 0, length);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
return format(bais, index);
}

/**
* The formatter function is called repeatedly to read all of the bytes
* in the source and append the output.
* The formatter function is called for the ByteBuffer's contents.
* The source bytes are from the {@code ByteBuffer.position()}
* to the {@code ByteBuffer.limit()}.
* The position is not modified.
@@ -464,7 +459,7 @@ public HexPrinter format(ByteBuffer source, int offset, int length) {
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public HexPrinter format(ByteBuffer source) {
return format(source, source.position(), source.limit());
return format(source, source.position(), source.limit() - source.position());
}

/**
@@ -544,37 +539,36 @@ public String toString(InputStream source) {
}

/**
* The formatter function is called repeatedly to read the bytes
* from the offset for the length and return a String.
* The ByteBuffer position and limit are unused and not modified.
* The formatter function is called for the range of the ByteBuffer contents
* and returned as a string.
* The ByteBuffer position is not used and not modified.
*
* @param source a ByteBuffer
* @param offset the offset in the ByteBuffer
* @param length the length in the ByteBuffer
* @param index the index in the ByteBuffer, must be non-negative and
* less than {@code limit()}.
* @param length the length in the ByteBuffer must be non-negative and
* no larger than {@code source.limit() - index}
* @return the output as a non-null {@code String}
* @throws java.io.UncheckedIOException if an I/O error occurs
* @throws java.lang.IndexOutOfBoundsException if the preconditions on
* {@code index} and {@code length} do not hold
*/
public String toString(ByteBuffer source, int offset, int length) {
public String toString(ByteBuffer source, int index, int length) {
Objects.requireNonNull(source, "ByteBuffer must be non-null");
byte[] bytes = new byte[length];
source.get(index, bytes, 0, length);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
StringBuilder sb = new StringBuilder();
ByteArrayInputStream bais;
if (source.hasArray() && !source.isReadOnly()) {
bais = new ByteArrayInputStream(source.array(), offset, length);
} else {
byte[] bytes = new byte[length];
source.get(bytes, offset, length);
bais = new ByteArrayInputStream(bytes);
}
try (AnnotationWriter writer =
new AnnotationWriter(this, bais, offset, sb)) {
new AnnotationWriter(this, bais, index, sb)) {
writer.flush();
return sb.toString();
}
}

/**
* The formatter function is called repeatedly to read all of the bytes
* in the source and return a String.
* The formatter function is called for the ByteBuffer contents
* and returned as a string.
* The source bytes are from the {@code ByteBuffer.position()}
* to the {@code ByteBuffer.limit()}.
* The position is not modified.
@@ -584,7 +578,7 @@ public String toString(ByteBuffer source, int offset, int length) {
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public String toString(ByteBuffer source) {
return toString(source, source.position(), source.limit());
return toString(source, source.position(), source.limit() - source.position());
}

/**
@@ -830,18 +824,20 @@ public interface Formatter {
}

/**
* Built-in formatters for printable byte, ASCII, UTF-8 and primitive types.
* Built-in formatters for printable byte, ASCII byte, UTF-8 and primitive types.
* Formatters for primitive types and different formatting options
* can be found by calling {@link #ofPrimitive(Class, String)}.
*/
public enum Formatters implements Formatter {
/**
* Read a byte and if it is ASCII write it,
* otherwise, write its mnemonic or its decimal value.
* Read a byte, return the value as a single character string
* if it is printable, otherwise return ".".
*/
PRINTABLE,
/**
* Read a byte, if it is ASCII write it, otherwise write a ".".
* Read a byte and return it as a string.
* Return the character if it is ASCII, return its mnemonic if it
* is a control character, otherwise return its decimal value as a string.
*/
ASCII,
/**
@@ -863,39 +859,39 @@ public void annotate(DataInputStream in, Appendable out) throws IOException {
}

/**
* Read a byte and write it as ASCII if it is printable,
* print its mnemonic if it is a control character,
* and print its decimal value otherwise.
* A space separator character is appended for control and decimal values.
* Read a byte and return it as a single character string if it is printable,
* otherwise return ".".
*
* @param in a DataInputStream
* @param out an Appendable to write to
* @throws IOException if an I/O error occurs
*/
static void bytePrintable(DataInputStream in, Appendable out) throws IOException {
int v = in.readUnsignedByte();
if (v < 32) {
out.append("\\").append(CONTROL_MNEMONICS[v]);
} else if (v < 126 && Character.isDefined(v)) {
if (!Character.isISOControl(v) && v < 127) {
out.append((char) v);
} else {
out.append("\\").append(Integer.toString(v, 10));
out.append('.');
}
}

/**
* Read a byte and write it as ASCII if it is printable, otherwise print ".".
* Read a byte and return it as a string.
* Append the byte if it is ASCII, its mnemonic if it
* is a control character, and otherwise its decimal value.
*
* @param in a DataInputStream
* @param out an Appendable to write to
* @throws IOException if an I/O error occurs
*/
static void byteASCII(DataInputStream in, Appendable out) throws IOException {
int v = in.readUnsignedByte();
if (Character.isDefined(v)) {
if (v < 32) {
out.append('\\').append(CONTROL_MNEMONICS[v]);
} else if (v < 127) {
out.append((char) v);
} else {
out.append('.');
out.append('\\').append(Integer.toString(v, 10));
}
}

0 comments on commit 120a0d1

Please sign in to comment.