View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertThrows;
21  
22  import java.io.IOException;
23  
24  import org.apache.commons.io.output.ByteArrayOutputStream;
25  import org.apache.commons.io.test.ThrowOnCloseOutputStream;
26  import org.junit.jupiter.api.Test;
27  
28  /**
29   * Tests {@link HexDump}.
30   */
31  public class HexDumpTest {
32  
33      @Test
34      public void testDumpAppendable() throws IOException {
35          final byte[] testArray = new byte[256];
36  
37          for (int j = 0; j < 256; j++) {
38              testArray[j] = (byte) j;
39          }
40  
41          // verify proper behavior dumping the entire array
42          StringBuilder out = new StringBuilder();
43          HexDump.dump(testArray, out);
44          assertEquals(
45              "00000000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................" + System.lineSeparator() +
46              "00000010 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ................" + System.lineSeparator() +
47              "00000020 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F  !\"#$%&'()*+,-./" + System.lineSeparator() +
48              "00000030 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?" + System.lineSeparator() +
49              "00000040 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO" + System.lineSeparator() +
50              "00000050 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F PQRSTUVWXYZ[\\]^_" + System.lineSeparator() +
51              "00000060 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F `abcdefghijklmno" + System.lineSeparator() +
52              "00000070 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F pqrstuvwxyz{|}~." + System.lineSeparator() +
53              "00000080 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F ................" + System.lineSeparator() +
54              "00000090 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F ................" + System.lineSeparator() +
55              "000000A0 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF ................" + System.lineSeparator() +
56              "000000B0 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF ................" + System.lineSeparator() +
57              "000000C0 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF ................" + System.lineSeparator() +
58              "000000D0 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF ................" + System.lineSeparator() +
59              "000000E0 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................" + System.lineSeparator() +
60              "000000F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................" + System.lineSeparator(),
61              out.toString());
62  
63          // verify proper behavior with non-zero offset, non-zero index and length shorter than array size
64          out = new StringBuilder();
65          HexDump.dump(testArray, 0x10000000, out, 0x28, 32);
66          assertEquals(
67              "10000028 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 ()*+,-./01234567" + System.lineSeparator() +
68              "10000038 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 89:;<=>?@ABCDEFG" + System.lineSeparator(),
69              out.toString());
70  
71          // verify proper behavior with non-zero index and length shorter than array size
72          out = new StringBuilder();
73          HexDump.dump(testArray, 0, out, 0x40, 24);
74          assertEquals(
75              "00000040 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ABCDEFGHIJKLMNO" + System.lineSeparator() +
76              "00000050 50 51 52 53 54 55 56 57                         PQRSTUVW" + System.lineSeparator(),
77              out.toString());
78  
79          // verify proper behavior with negative index
80          assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0x10000000, new StringBuilder(), -1, testArray.length));
81  
82          // verify proper behavior with index that is too large
83          assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0x10000000, new StringBuilder(), testArray.length, testArray.length));
84  
85          // verify proper behavior with length that is negative
86          assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0, new StringBuilder(), 0, -1));
87  
88          // verify proper behavior with length that is too large
89          final Exception exception = assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0, new StringBuilder(), 1,
90              testArray.length));
91          assertEquals("Range [1, 1 + 256) out of bounds for length 256", exception.getMessage());
92  
93          // verify proper behavior with null appendable
94          assertThrows(NullPointerException.class, () -> HexDump.dump(testArray, 0x10000000, null, 0, testArray.length));
95      }
96  
97      @Test
98      public void testDumpOutputStream() throws IOException {
99          final byte[] testArray = new byte[256];
100 
101         for (int j = 0; j < 256; j++) {
102             testArray[j] = (byte) j;
103         }
104         ByteArrayOutputStream stream = new ByteArrayOutputStream();
105 
106         HexDump.dump(testArray, 0, stream, 0);
107         byte[] outputArray = new byte[16 * (73 + System.lineSeparator().length())];
108 
109         for (int j = 0; j < 16; j++) {
110             int offset = (73 + System.lineSeparator().length()) * j;
111 
112             outputArray[offset++] = (byte) '0';
113             outputArray[offset++] = (byte) '0';
114             outputArray[offset++] = (byte) '0';
115             outputArray[offset++] = (byte) '0';
116             outputArray[offset++] = (byte) '0';
117             outputArray[offset++] = (byte) '0';
118             outputArray[offset++] = (byte) toHex(j);
119             outputArray[offset++] = (byte) '0';
120             outputArray[offset++] = (byte) ' ';
121             for (int k = 0; k < 16; k++) {
122                 outputArray[offset++] = (byte) toHex(j);
123                 outputArray[offset++] = (byte) toHex(k);
124                 outputArray[offset++] = (byte) ' ';
125             }
126             for (int k = 0; k < 16; k++) {
127                 outputArray[offset++] = (byte) toAscii(j * 16 + k);
128             }
129             System.arraycopy(System.lineSeparator().getBytes(), 0, outputArray, offset, System.lineSeparator().getBytes().length);
130         }
131         byte[] actualOutput = stream.toByteArray();
132 
133         assertEquals(outputArray.length, actualOutput.length, "array size mismatch");
134         for (int j = 0; j < outputArray.length; j++) {
135             assertEquals(outputArray[j], actualOutput[j], "array[ " + j + "] mismatch");
136         }
137 
138         // verify proper behavior with non-zero offset
139         stream = new ByteArrayOutputStream();
140         HexDump.dump(testArray, 0x10000000, stream, 0);
141         outputArray = new byte[16 * (73 + System.lineSeparator().length())];
142         for (int j = 0; j < 16; j++) {
143             int offset = (73 + System.lineSeparator().length()) * j;
144 
145             outputArray[offset++] = (byte) '1';
146             outputArray[offset++] = (byte) '0';
147             outputArray[offset++] = (byte) '0';
148             outputArray[offset++] = (byte) '0';
149             outputArray[offset++] = (byte) '0';
150             outputArray[offset++] = (byte) '0';
151             outputArray[offset++] = (byte) toHex(j);
152             outputArray[offset++] = (byte) '0';
153             outputArray[offset++] = (byte) ' ';
154             for (int k = 0; k < 16; k++) {
155                 outputArray[offset++] = (byte) toHex(j);
156                 outputArray[offset++] = (byte) toHex(k);
157                 outputArray[offset++] = (byte) ' ';
158             }
159             for (int k = 0; k < 16; k++) {
160                 outputArray[offset++] = (byte) toAscii(j * 16 + k);
161             }
162             System.arraycopy(System.lineSeparator().getBytes(), 0, outputArray, offset,
163                     System.lineSeparator().getBytes().length);
164         }
165         actualOutput = stream.toByteArray();
166         assertEquals(outputArray.length, actualOutput.length, "array size mismatch");
167         for (int j = 0; j < outputArray.length; j++) {
168             assertEquals(outputArray[j], actualOutput[j], "array[ " + j + "] mismatch");
169         }
170 
171         // verify proper behavior with negative offset
172         stream = new ByteArrayOutputStream();
173         HexDump.dump(testArray, 0xFF000000, stream, 0);
174         outputArray = new byte[16 * (73 + System.lineSeparator().length())];
175         for (int j = 0; j < 16; j++) {
176             int offset = (73 + System.lineSeparator().length()) * j;
177 
178             outputArray[offset++] = (byte) 'F';
179             outputArray[offset++] = (byte) 'F';
180             outputArray[offset++] = (byte) '0';
181             outputArray[offset++] = (byte) '0';
182             outputArray[offset++] = (byte) '0';
183             outputArray[offset++] = (byte) '0';
184             outputArray[offset++] = (byte) toHex(j);
185             outputArray[offset++] = (byte) '0';
186             outputArray[offset++] = (byte) ' ';
187             for (int k = 0; k < 16; k++) {
188                 outputArray[offset++] = (byte) toHex(j);
189                 outputArray[offset++] = (byte) toHex(k);
190                 outputArray[offset++] = (byte) ' ';
191             }
192             for (int k = 0; k < 16; k++) {
193                 outputArray[offset++] = (byte) toAscii(j * 16 + k);
194             }
195             System.arraycopy(System.lineSeparator().getBytes(), 0, outputArray, offset,
196                     System.lineSeparator().getBytes().length);
197         }
198         actualOutput = stream.toByteArray();
199         assertEquals(outputArray.length, actualOutput.length, "array size mismatch");
200         for (int j = 0; j < outputArray.length; j++) {
201             assertEquals(outputArray[j], actualOutput[j], "array[ " + j + "] mismatch");
202         }
203 
204         // verify proper behavior with non-zero index
205         stream = new ByteArrayOutputStream();
206         HexDump.dump(testArray, 0x10000000, stream, 0x81);
207         outputArray = new byte[8 * (73 + System.lineSeparator().length()) - 1];
208         for (int j = 0; j < 8; j++) {
209             int offset = (73 + System.lineSeparator().length()) * j;
210 
211             outputArray[offset++] = (byte) '1';
212             outputArray[offset++] = (byte) '0';
213             outputArray[offset++] = (byte) '0';
214             outputArray[offset++] = (byte) '0';
215             outputArray[offset++] = (byte) '0';
216             outputArray[offset++] = (byte) '0';
217             outputArray[offset++] = (byte) toHex(j + 8);
218             outputArray[offset++] = (byte) '1';
219             outputArray[offset++] = (byte) ' ';
220             for (int k = 0; k < 16; k++) {
221                 final int index = 0x81 + j * 16 + k;
222 
223                 if (index < 0x100) {
224                     outputArray[offset++] = (byte) toHex(index / 16);
225                     outputArray[offset++] = (byte) toHex(index);
226                 } else {
227                     outputArray[offset++] = (byte) ' ';
228                     outputArray[offset++] = (byte) ' ';
229                 }
230                 outputArray[offset++] = (byte) ' ';
231             }
232             for (int k = 0; k < 16; k++) {
233                 final int index = 0x81 + j * 16 + k;
234 
235                 if (index < 0x100) {
236                     outputArray[offset++] = (byte) toAscii(index);
237                 }
238             }
239             System.arraycopy(System.lineSeparator().getBytes(), 0, outputArray, offset,
240                     System.lineSeparator().getBytes().length);
241         }
242         actualOutput = stream.toByteArray();
243         assertEquals(outputArray.length, actualOutput.length, "array size mismatch");
244         for (int j = 0; j < outputArray.length; j++) {
245             assertEquals(outputArray[j], actualOutput[j], "array[ " + j + "] mismatch");
246         }
247 
248         // verify proper behavior with negative index
249         assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(), -1));
250 
251         // verify proper behavior with index that is too large
252         assertThrows(ArrayIndexOutOfBoundsException.class, () -> HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(), testArray.length));
253 
254         // verify proper behavior with null stream
255         assertThrows(NullPointerException.class, () -> HexDump.dump(testArray, 0x10000000, null, 0));
256 
257         // verify output stream is not closed by the dump method
258         HexDump.dump(testArray, 0, new ThrowOnCloseOutputStream(new ByteArrayOutputStream()), 0);
259     }
260 
261     private char toAscii(final int c) {
262         char rval = '.';
263 
264         if (c >= 32 && c <= 126) {
265             rval = (char) c;
266         }
267         return rval;
268     }
269 
270     private char toHex(final int n) {
271         final char[] hexChars =
272                 {
273                     '0', '1', '2', '3', '4', '5', '6', '7',
274                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
275                 };
276 
277         return hexChars[n % 16];
278     }
279 }