001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.io.OutputStreamWriter; 022import java.nio.charset.Charset; 023import java.util.Objects; 024 025import org.apache.commons.io.output.CloseShieldOutputStream; 026 027/** 028 * Dumps data in hexadecimal format. 029 * <p> 030 * Provides a single function to take an array of bytes and display it 031 * in hexadecimal form. 032 * </p> 033 * <p> 034 * Provenance: POI. 035 * </p> 036 */ 037public class HexDump { 038 039 /** 040 * The line-separator (initializes to "line.separator" system property). 041 * 042 * @deprecated Use {@link System#lineSeparator()}. 043 */ 044 @Deprecated 045 public static final String EOL = System.lineSeparator(); 046 047 private static final char[] HEX_CODES = 048 { 049 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 050 'A', 'B', 'C', 'D', 'E', 'F' 051 }; 052 053 private static final int[] SHIFTS = 054 { 055 28, 24, 20, 16, 12, 8, 4, 0 056 }; 057 058 /** 059 * Dumps an array of bytes to an Appendable. The output is formatted 060 * for human inspection, with a hexadecimal offset followed by the 061 * hexadecimal values of the next 16 bytes of data and the printable ASCII 062 * characters (if any) that those bytes represent printed per each line 063 * of output. 064 * 065 * @param data the byte array to be dumped 066 * @param appendable the Appendable to which the data is to be written 067 * @throws IOException is thrown if anything goes wrong writing 068 * the data to appendable 069 * @throws NullPointerException if the output appendable is null 070 * @since 2.12.0 071 */ 072 public static void dump(final byte[] data, final Appendable appendable) 073 throws IOException { 074 dump(data, 0, appendable, 0, data.length); 075 } 076 077 /** 078 * Dumps an array of bytes to an Appendable. The output is formatted 079 * for human inspection, with a hexadecimal offset followed by the 080 * hexadecimal values of the next 16 bytes of data and the printable ASCII 081 * characters (if any) that those bytes represent printed per each line 082 * of output. 083 * <p> 084 * The offset argument specifies the start offset of the data array 085 * within a larger entity like a file or an incoming stream. For example, 086 * if the data array contains the third kibibyte of a file, then the 087 * offset argument should be set to 2048. The offset value printed 088 * at the beginning of each line indicates where in that larger entity 089 * the first byte on that line is located. 090 * </p> 091 * 092 * @param data the byte array to be dumped 093 * @param offset offset of the byte array within a larger entity 094 * @param appendable the Appendable to which the data is to be written 095 * @param index initial index into the byte array 096 * @param length number of bytes to dump from the array 097 * @throws IOException is thrown if anything goes wrong writing 098 * the data to appendable 099 * @throws ArrayIndexOutOfBoundsException if the index or length is 100 * outside the data array's bounds 101 * @throws NullPointerException if the output appendable is null 102 * @since 2.12.0 103 */ 104 public static void dump(final byte[] data, final long offset, 105 final Appendable appendable, final int index, 106 final int length) 107 throws IOException, ArrayIndexOutOfBoundsException { 108 Objects.requireNonNull(appendable, "appendable"); 109 if (index < 0 || index >= data.length) { 110 throw new ArrayIndexOutOfBoundsException( 111 "illegal index: " + index + " into array of length " 112 + data.length); 113 } 114 long display_offset = offset + index; 115 final StringBuilder buffer = new StringBuilder(74); 116 117 // TODO Use Objects.checkFromIndexSize(index, length, data.length) when upgrading to JDK9 118 if (length < 0 || index + length > data.length) { 119 throw new ArrayIndexOutOfBoundsException(String.format("Range [%s, %<s + %s) out of bounds for length %s", index, length, data.length)); 120 } 121 122 final int endIndex = index + length; 123 124 for (int j = index; j < endIndex; j += 16) { 125 int chars_read = endIndex - j; 126 127 if (chars_read > 16) { 128 chars_read = 16; 129 } 130 dump(buffer, display_offset).append(' '); 131 for (int k = 0; k < 16; k++) { 132 if (k < chars_read) { 133 dump(buffer, data[k + j]); 134 } else { 135 buffer.append(" "); 136 } 137 buffer.append(' '); 138 } 139 for (int k = 0; k < chars_read; k++) { 140 if (data[k + j] >= ' ' && data[k + j] < 127) { 141 buffer.append((char) data[k + j]); 142 } else { 143 buffer.append('.'); 144 } 145 } 146 buffer.append(System.lineSeparator()); 147 appendable.append(buffer); 148 buffer.setLength(0); 149 display_offset += chars_read; 150 } 151 } 152 153 /** 154 * Dumps an array of bytes to an OutputStream. The output is formatted 155 * for human inspection, with a hexadecimal offset followed by the 156 * hexadecimal values of the next 16 bytes of data and the printable ASCII 157 * characters (if any) that those bytes represent printed per each line 158 * of output. 159 * <p> 160 * The offset argument specifies the start offset of the data array 161 * within a larger entity like a file or an incoming stream. For example, 162 * if the data array contains the third kibibyte of a file, then the 163 * offset argument should be set to 2048. The offset value printed 164 * at the beginning of each line indicates where in that larger entity 165 * the first byte on that line is located. 166 * </p> 167 * <p> 168 * All bytes between the given index (inclusive) and the end of the 169 * data array are dumped. 170 * </p> 171 * <p> 172 * This method uses the virtual machine's {@link Charset#defaultCharset() default charset}. 173 * </p> 174 * 175 * @param data the byte array to be dumped 176 * @param offset offset of the byte array within a larger entity 177 * @param stream the OutputStream to which the data is to be 178 * written 179 * @param index initial index into the byte array 180 * @throws IOException is thrown if anything goes wrong writing 181 * the data to stream 182 * @throws ArrayIndexOutOfBoundsException if the index is 183 * outside the data array's bounds 184 * @throws NullPointerException if the output stream is null 185 */ 186 @SuppressWarnings("resource") // Caller closes stream 187 public static void dump(final byte[] data, final long offset, 188 final OutputStream stream, final int index) 189 throws IOException, ArrayIndexOutOfBoundsException { 190 Objects.requireNonNull(stream, "stream"); 191 192 try (OutputStreamWriter out = new OutputStreamWriter(CloseShieldOutputStream.wrap(stream), Charset.defaultCharset())) { 193 dump(data, offset, out, index, data.length - index); 194 } 195 } 196 197 /** 198 * Dumps a byte value into a StringBuilder. 199 * 200 * @param builder the StringBuilder to dump the value in 201 * @param value the byte value to be dumped 202 * @return StringBuilder containing the dumped value. 203 */ 204 private static StringBuilder dump(final StringBuilder builder, final byte value) { 205 for (int j = 0; j < 2; j++) { 206 builder.append(HEX_CODES[value >> SHIFTS[j + 6] & 15]); 207 } 208 return builder; 209 } 210 211 /** 212 * Dumps a long value into a StringBuilder. 213 * 214 * @param builder the StringBuilder to dump the value in 215 * @param value the long value to be dumped 216 * @return StringBuilder containing the dumped value. 217 */ 218 private static StringBuilder dump(final StringBuilder builder, final long value) { 219 for (int j = 0; j < 8; j++) { 220 builder.append(HEX_CODES[(int) (value >> SHIFTS[j]) & 15]); 221 } 222 return builder; 223 } 224 225 /** 226 * Instances should NOT be constructed in standard programming. 227 */ 228 public HexDump() { 229 } 230 231}