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 java.io.IOException; 20 import java.io.OutputStream; 21 import java.io.OutputStreamWriter; 22 import java.nio.charset.Charset; 23 import java.util.Objects; 24 25 import org.apache.commons.io.output.CloseShieldOutputStream; 26 27 /** 28 * Dumps data in hexadecimal format. 29 * <p> 30 * Provides a single function to take an array of bytes and display it 31 * in hexadecimal form. 32 * </p> 33 * <p> 34 * Provenance: POI. 35 * </p> 36 */ 37 public class HexDump { 38 39 /** 40 * The line-separator (initializes to "line.separator" system property). 41 * 42 * @deprecated Use {@link System#lineSeparator()}. 43 */ 44 @Deprecated 45 public static final String EOL = System.lineSeparator(); 46 47 private static final char[] HEX_CODES = 48 { 49 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 50 'A', 'B', 'C', 'D', 'E', 'F' 51 }; 52 53 private static final int[] SHIFTS = 54 { 55 28, 24, 20, 16, 12, 8, 4, 0 56 }; 57 58 /** 59 * Dumps an array of bytes to an Appendable. The output is formatted 60 * for human inspection, with a hexadecimal offset followed by the 61 * hexadecimal values of the next 16 bytes of data and the printable ASCII 62 * characters (if any) that those bytes represent printed per each line 63 * of output. 64 * 65 * @param data the byte array to be dumped 66 * @param appendable the Appendable to which the data is to be written 67 * 68 * @throws IOException is thrown if anything goes wrong writing 69 * the data to appendable 70 * @throws NullPointerException if the output appendable is null 71 * 72 * @since 2.12.0 73 */ 74 public static void dump(final byte[] data, final Appendable appendable) 75 throws IOException { 76 dump(data, 0, appendable, 0, data.length); 77 } 78 79 /** 80 * Dumps an array of bytes to an Appendable. The output is formatted 81 * for human inspection, with a hexadecimal offset followed by the 82 * hexadecimal values of the next 16 bytes of data and the printable ASCII 83 * characters (if any) that those bytes represent printed per each line 84 * of output. 85 * <p> 86 * The offset argument specifies the start offset of the data array 87 * within a larger entity like a file or an incoming stream. For example, 88 * if the data array contains the third kibibyte of a file, then the 89 * offset argument should be set to 2048. The offset value printed 90 * at the beginning of each line indicates where in that larger entity 91 * the first byte on that line is located. 92 * </p> 93 * 94 * @param data the byte array to be dumped 95 * @param offset offset of the byte array within a larger entity 96 * @param appendable the Appendable to which the data is to be written 97 * @param index initial index into the byte array 98 * @param length number of bytes to dump from the array 99 * 100 * @throws IOException is thrown if anything goes wrong writing 101 * the data to appendable 102 * @throws ArrayIndexOutOfBoundsException if the index or length is 103 * outside the data array's bounds 104 * @throws NullPointerException if the output appendable is null 105 * 106 * @since 2.12.0 107 */ 108 public static void dump(final byte[] data, final long offset, 109 final Appendable appendable, final int index, 110 final int length) 111 throws IOException, ArrayIndexOutOfBoundsException { 112 Objects.requireNonNull(appendable, "appendable"); 113 if (index < 0 || index >= data.length) { 114 throw new ArrayIndexOutOfBoundsException( 115 "illegal index: " + index + " into array of length " 116 + data.length); 117 } 118 long display_offset = offset + index; 119 final StringBuilder buffer = new StringBuilder(74); 120 121 // TODO Use Objects.checkFromIndexSize(index, length, data.length) when upgrading to JDK9 122 if (length < 0 || index + length > data.length) { 123 throw new ArrayIndexOutOfBoundsException(String.format("Range [%s, %<s + %s) out of bounds for length %s", index, length, data.length)); 124 } 125 126 final int endIndex = index + length; 127 128 for (int j = index; j < endIndex; j += 16) { 129 int chars_read = endIndex - j; 130 131 if (chars_read > 16) { 132 chars_read = 16; 133 } 134 dump(buffer, display_offset).append(' '); 135 for (int k = 0; k < 16; k++) { 136 if (k < chars_read) { 137 dump(buffer, data[k + j]); 138 } else { 139 buffer.append(" "); 140 } 141 buffer.append(' '); 142 } 143 for (int k = 0; k < chars_read; k++) { 144 if (data[k + j] >= ' ' && data[k + j] < 127) { 145 buffer.append((char) data[k + j]); 146 } else { 147 buffer.append('.'); 148 } 149 } 150 buffer.append(System.lineSeparator()); 151 appendable.append(buffer); 152 buffer.setLength(0); 153 display_offset += chars_read; 154 } 155 } 156 157 /** 158 * Dumps an array of bytes to an OutputStream. The output is formatted 159 * for human inspection, with a hexadecimal offset followed by the 160 * hexadecimal values of the next 16 bytes of data and the printable ASCII 161 * characters (if any) that those bytes represent printed per each line 162 * of output. 163 * <p> 164 * The offset argument specifies the start offset of the data array 165 * within a larger entity like a file or an incoming stream. For example, 166 * if the data array contains the third kibibyte of a file, then the 167 * offset argument should be set to 2048. The offset value printed 168 * at the beginning of each line indicates where in that larger entity 169 * the first byte on that line is located. 170 * </p> 171 * <p> 172 * All bytes between the given index (inclusive) and the end of the 173 * data array are dumped. 174 * </p> 175 * 176 * @param data the byte array to be dumped 177 * @param offset offset of the byte array within a larger entity 178 * @param stream the OutputStream to which the data is to be 179 * written 180 * @param index initial index into the byte array 181 * 182 * @throws IOException is thrown if anything goes wrong writing 183 * the data to stream 184 * @throws ArrayIndexOutOfBoundsException if the index is 185 * outside the data array's bounds 186 * @throws NullPointerException if the output stream is null 187 */ 188 @SuppressWarnings("resource") // Caller closes stream 189 public static void dump(final byte[] data, final long offset, 190 final OutputStream stream, final int index) 191 throws IOException, ArrayIndexOutOfBoundsException { 192 Objects.requireNonNull(stream, "stream"); 193 194 try (OutputStreamWriter out = new OutputStreamWriter(CloseShieldOutputStream.wrap(stream), Charset.defaultCharset())) { 195 dump(data, offset, out, index, data.length - index); 196 } 197 } 198 199 /** 200 * Dumps a byte value into a StringBuilder. 201 * 202 * @param _cbuffer the StringBuilder to dump the value in 203 * @param value the byte value to be dumped 204 * @return StringBuilder containing the dumped value. 205 */ 206 private static StringBuilder dump(final StringBuilder _cbuffer, final byte value) { 207 for (int j = 0; j < 2; j++) { 208 _cbuffer.append(HEX_CODES[value >> SHIFTS[j + 6] & 15]); 209 } 210 return _cbuffer; 211 } 212 213 /** 214 * Dumps a long value into a StringBuilder. 215 * 216 * @param _lbuffer the StringBuilder to dump the value in 217 * @param value the long value to be dumped 218 * @return StringBuilder containing the dumped value. 219 */ 220 private static StringBuilder dump(final StringBuilder _lbuffer, final long value) { 221 for (int j = 0; j < 8; j++) { 222 _lbuffer 223 .append(HEX_CODES[(int) (value >> SHIFTS[j]) & 15]); 224 } 225 return _lbuffer; 226 } 227 228 /** 229 * Instances should NOT be constructed in standard programming. 230 */ 231 public HexDump() { 232 } 233 234 }