/* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.*; import java.nio.*; import java.util.*; import java.util.stream.*; import static java.nio.charset.StandardCharsets.*; import static java.util.Hex.*; /** * @test * @bug 8170769 * @summary test hexadecimal conversions to/from binary data. */ public class HexdumpTest { private static final String LINE_SEPARATOR = String.format("%n"); /** * Formatter that generates a custom hexdump format (8-byte chunks). */ public static final Hex.Formatter CUSTOM_8_HEXDUMP_FORMATTER = new Hex.Formatter() { public String format(long offset, byte[] chunk, int fromIndex, int toIndex) { return String.format("%04d %-16s %s", offset, Hex.toString(chunk, fromIndex, toIndex), Hex.toPrintableString(chunk, fromIndex, toIndex)); } }; /** * Formatter that generates a custom hexdump format (32-byte chunks). */ public static final Hex.Formatter CUSTOM_32_HEXDUMP_FORMATTER = new Hex.Formatter() { public String format(long offset, byte[] chunk, int fromIndex, int toIndex) { return String.format("%04d %-64s %s", offset, Hex.toString(chunk, fromIndex, toIndex), Hex.toPrintableString(chunk, fromIndex, toIndex)); } }; /** * Formatter that generates a custom hexdump format (supports Latin-1). */ public static final Hex.Formatter CUSTOM_LATIN1_HEXDUMP_FORMATTER = new Hex.Formatter() { public String format(long offset, byte[] chunk, int fromIndex, int toIndex) { return String.format("%04d %s %s", offset, Hex.toFormattedHexString(chunk, fromIndex, toIndex), new String(chunk, fromIndex, toIndex - fromIndex, ISO_8859_1).replaceAll("[\\x00-\\x1F\\x7F]", ".")); } }; public static final void main(String[] args) throws Exception { // Test data for byte array List byteArrayData = new ArrayList<>() {{ add(new byte[]{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 }); add(new byte[]{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }); add(new byte[]{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 }); add(new byte[]{ (byte)0, (byte)0, (byte)134, (byte)0, (byte)61 }); add(new byte[]{ (byte)0x00, (byte)0x01, (byte)0x02 }); add(new byte[]{ (byte)0x00, (byte)0x01 }); add(new byte[]{ (byte)0x00 }); add(new byte[0]); add(new byte[]{ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69, 70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90, 91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108, 109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, 125,126,127 }); }}; // Test data for byte array (Latin1) List latin1ByteArrayData = new ArrayList<>() {{ add(new byte[]{ (byte)192, (byte)193, (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, (byte)200, (byte)201, (byte)202, (byte)203, (byte)204, (byte)205, (byte)206, (byte)207 }); add(new byte[]{ (byte)192, 1, (byte)193, 2, (byte)194, 3, (byte)195, 4, (byte)196, 5, (byte)197, 6 }); add(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, (byte)192, (byte)193, (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, (byte)200, (byte)201, (byte)202, (byte)203, (byte)204, (byte)205, (byte)206, (byte)207 }); }}; // Test data for String List stringData = new ArrayList<>() {{ add("000102030405060708090a0b0c0d0e0f101112"); add("000102030405060708090a0b0c0d0e0f"); add("000102030405060708090a0b0c0d0e"); add("000086003d"); add("000102"); add("0001"); add("00"); add(""); add("202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f"); }}; // Test data for Stream of String List> streamData = new ArrayList<>() {{ add(List.of( "00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|", "00000010 10 11 12 |...|")); add(List.of( "00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|" )); add(List.of( "00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e |...............|" )); add(List.of( "00000000 00 00 86 00 3d |....=|")); add(List.of( "00000000 00 01 02 |...|")); add(List.of( "00000000 00 01 |..|")); add(List.of( "00000000 00 |.|")); add(Collections.emptyList()); add(List.of( "00000000 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !\"#$%&'()*+,-./|", "00000010 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|", "00000020 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|", "00000030 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\\]^_|", "00000040 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|", "00000050 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|" )); }}; // Test data for Stream of String (subarray) List> subarrayStreamData = new ArrayList<>() {{ add(List.of( "00000000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 |................|", "00000010 11 |.|")); add(List.of( "00000000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e |..............|" )); add(List.of( "00000000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d |.............|" )); add(List.of( "00000000 00 86 00 |...|")); add(List.of( "00000000 01 |.|")); add(Collections.emptyList()); // skipped, too short add(Collections.emptyList()); // skipped, too short add(Collections.emptyList()); // skipped, too short add(List.of( "00000000 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!\"#$%&'()*+,-./0|", "00000010 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 |123456789:;<=>?@|", "00000020 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 |ABCDEFGHIJKLMNOP|", "00000030 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 |QRSTUVWXYZ[\\]^_`|", "00000040 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 |abcdefghijklmnop|", "00000050 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e |qrstuvwxyz{|}~|" )); }}; // Test data for Stream of custom String List> customStreamData = new ArrayList<>() {{ add(List.of( "0000 000102030405060708090a0b0c0d0e0f101112 ...................")); add(List.of( "0000 000102030405060708090a0b0c0d0e0f ................")); add(List.of( "0000 000102030405060708090a0b0c0d0e ...............")); add(List.of( "0000 000086003d ....=")); add(List.of( "0000 000102 ...")); add(List.of( "0000 0001 ..")); add(List.of( "0000 00 .")); add(Collections.emptyList()); add(List.of( "0000 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f !\"#$%&'()*+,-./0123456789:;<=>?", "0032 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", "0064 606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f `abcdefghijklmnopqrstuvwxyz{|}~." )); }}; // Test data for Stream of custom Latin-1 String List> customLatin1StreamData = new ArrayList<>() {{ add(List.of( "0000 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ" )); add(List.of( "0000 c0 01 c1 02 c2 03 c3 04 c4 05 c5 06 À.Á.Â.Ã.Ä.Å." )); add(List.of( "0000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................", "0016 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ" )); }}; // Test data for Stream of custom String (byteBuffer) List> byteBufferStreamData = new ArrayList<>() {{ add(List.of( "0000 0001020304050607 ........", "0008 08090a0b0c0d0e0f ........", "0016 101112 ..." )); add(List.of( "0000 0001020304050607 ........", "0008 08090a0b0c0d0e0f ........" )); add(List.of( "0000 0001020304050607 ........", "0008 08090a0b0c0d0e ......." )); add(List.of( "0000 000086003d ....=" )); add(List.of( "0000 000102 ..." )); add(List.of( "0000 0001 .." )); add(List.of( "0000 00 ." )); add(Collections.emptyList()); add(List.of( "0000 2021222324252627 !\"#$%&'", "0008 28292a2b2c2d2e2f ()*+,-./", "0016 3031323334353637 01234567", "0024 38393a3b3c3d3e3f 89:;<=>?", "0032 4041424344454647 @ABCDEFG", "0040 48494a4b4c4d4e4f HIJKLMNO", "0048 5051525354555657 PQRSTUVW", "0056 58595a5b5c5d5e5f XYZ[\\]^_", "0064 6061626364656667 `abcdefg", "0072 68696a6b6c6d6e6f hijklmno", "0080 7071727374757677 pqrstuvw", "0088 78797a7b7c7d7e7f xyz{|}~." )); }}; // Testing byte array conversions to hex string System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); String expected = stringData.get(i); String output = Hex.toString(input); if (expected.equals(output)) { System.out.println((i + 1) + ") Generated hex string: \"" + output + "\""); } else { throw new Exception("ERROR: expected: \"" + expected + "\" but received: \"" + output + "\""); } } // Testing subarray conversions to hex string System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); if (input.length < 2) { System.out.println((i + 1) + ") Input too short - skipping..."); continue; } String expected = stringData.get(i).toLowerCase(); expected = expected.substring(2, expected.length() - 2); String output = Hex.toString(input, 1, input.length - 1); if (expected.equals(output)) { System.out.println((i + 1) + ") Generated subarray hex string: \"" + output + "\""); } else { throw new Exception("ERROR: expected: \"" + expected + "\" but received: \"" + output + "\""); } } // Testing conversions from hex string System.out.println("----------"); for (int i = 0; i < stringData.size(); i++) { String input = stringData.get(i); byte[] expected = byteArrayData.get(i); byte[] output = Hex.fromString(input); if (Arrays.equals(expected, output)) { System.out.println((i + 1) + ") Parsed hex string: \"" + input + "\""); } else { throw new Exception("ERROR: expected: " + Arrays.toString(expected) + " but received: " + Arrays.toString(output)); } } // Testing conversions to stream of hexdump string System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); Stream expected = Stream.of(streamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(input); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of hexdump string: (from byte array)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(input).forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString(Hex.dumpAsStream(input).toArray())); } } // Testing subarray conversions to stream of hexdump string System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); if (input.length < 2) { System.out.println((i + 1) + ") Input too short - skipping..."); continue; } Stream expected = Stream.of(subarrayStreamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(input, 1, input.length - 1, 16, null); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of hexdump string: (from byte subarray)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(input, 1, input.length - 1, 16, null) .forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString( Hex.dumpAsStream(input, 1, input.length - 1, 16, null) .toArray())); } } // Testing subarray conversions to stream of hexdump string System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); if (input.length < 2) { System.out.println((i + 1) + ") Input too short - skipping..."); continue; } Stream expected = Stream.of(subarrayStreamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(Arrays.copyOfRange(input, 1, input.length - 1)); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of hexdump string: (from byte subarray)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(Arrays.copyOfRange(input, 1, input.length - 1)) .forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString( Hex.dumpAsStream(Arrays.copyOfRange(input, 1, input.length - 1)) .toArray())); } } // Testing conversions to stream of custom hexdump string using 32-byte chunks System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); Stream expected = Stream.of(customStreamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(input, 0, input.length, 32, CUSTOM_32_HEXDUMP_FORMATTER); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of custom hexdump string: (from byte array)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(input, 0, input.length, 32, CUSTOM_32_HEXDUMP_FORMATTER) .forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString(Hex.dumpAsStream(input, 0, input.length, 32, CUSTOM_32_HEXDUMP_FORMATTER) .toArray())); } } // Testing conversions to stream of custom hexdump string using Latin-1 System.out.println("----------"); for (int i = 0; i < latin1ByteArrayData.size(); i++) { byte[] input = latin1ByteArrayData.get(i); Stream expected = Stream.of(customLatin1StreamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(input, 0, input.length, 16, CUSTOM_LATIN1_HEXDUMP_FORMATTER); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of custom Latin-1 hexdump string: (from byte array)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(input, 0, input.length, 16, CUSTOM_LATIN1_HEXDUMP_FORMATTER) .forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString(Hex.dumpAsStream(input, 0, input.length, 16, CUSTOM_LATIN1_HEXDUMP_FORMATTER) .toArray())); } } // Testing ByteBuffer conversions to stream of custom hexdump string using 8-byte chunks System.out.println("----------"); for (int i = 0; i < byteArrayData.size(); i++) { byte[] input = byteArrayData.get(i); Stream expected = Stream.of(byteBufferStreamData.get(i).toArray(new String[0])); Stream output = Hex.dumpAsStream(ByteBuffer.wrap(input), 0, input.length, 8, CUSTOM_8_HEXDUMP_FORMATTER); Object[] expectedArray = expected.toArray(); System.out.println((i + 1) + ") Generating stream of custom hexdump string: (from ByteBuffer)"); if (Arrays.equals(expectedArray, output.toArray())) { Hex.dumpAsStream(ByteBuffer.wrap(input), 0, input.length, 8, CUSTOM_8_HEXDUMP_FORMATTER) .forEach(System.out::println); } else { throw new Exception( "ERROR: expected this stream of custom hexdump string: " + Arrays.toString(expectedArray) + " but received: " + Arrays.toString( Hex.dumpAsStream(ByteBuffer.wrap(input), 0, input.length, 8, CUSTOM_8_HEXDUMP_FORMATTER).toArray())); } } } }