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.nio.charset.Charset; 022 023/** 024 * Dumps data in hexadecimal format. 025 * <p> 026 * Provides a single function to take an array of bytes and display it 027 * in hexadecimal form. 028 * <p> 029 * Origin of code: POI. 030 * 031 * @version $Id: HexDump.java 1471767 2013-04-24 23:24:19Z sebb $ 032 */ 033public class HexDump { 034 035 /** 036 * Instances should NOT be constructed in standard programming. 037 */ 038 public HexDump() { 039 super(); 040 } 041 042 /** 043 * Dump an array of bytes to an OutputStream. The output is formatted 044 * for human inspection, with a hexadecimal offset followed by the 045 * hexadecimal values of the next 16 bytes of data and the printable ASCII 046 * characters (if any) that those bytes represent printed per each line 047 * of output. 048 * <p> 049 * The offset argument specifies the start offset of the data array 050 * within a larger entity like a file or an incoming stream. For example, 051 * if the data array contains the third kibibyte of a file, then the 052 * offset argument should be set to 2048. The offset value printed 053 * at the beginning of each line indicates where in that larger entity 054 * the first byte on that line is located. 055 * <p> 056 * All bytes between the given index (inclusive) and the end of the 057 * data array are dumped. 058 * 059 * @param data the byte array to be dumped 060 * @param offset offset of the byte array within a larger entity 061 * @param stream the OutputStream to which the data is to be 062 * written 063 * @param index initial index into the byte array 064 * 065 * @throws IOException is thrown if anything goes wrong writing 066 * the data to stream 067 * @throws ArrayIndexOutOfBoundsException if the index is 068 * outside the data array's bounds 069 * @throws IllegalArgumentException if the output stream is null 070 */ 071 072 public static void dump(final byte[] data, final long offset, 073 final OutputStream stream, final int index) 074 throws IOException, ArrayIndexOutOfBoundsException, 075 IllegalArgumentException { 076 077 if (index < 0 || index >= data.length) { 078 throw new ArrayIndexOutOfBoundsException( 079 "illegal index: " + index + " into array of length " 080 + data.length); 081 } 082 if (stream == null) { 083 throw new IllegalArgumentException("cannot write to nullstream"); 084 } 085 long display_offset = offset + index; 086 final StringBuilder buffer = new StringBuilder(74); 087 088 for (int j = index; j < data.length; j += 16) { 089 int chars_read = data.length - j; 090 091 if (chars_read > 16) { 092 chars_read = 16; 093 } 094 dump(buffer, display_offset).append(' '); 095 for (int k = 0; k < 16; k++) { 096 if (k < chars_read) { 097 dump(buffer, data[k + j]); 098 } else { 099 buffer.append(" "); 100 } 101 buffer.append(' '); 102 } 103 for (int k = 0; k < chars_read; k++) { 104 if (data[k + j] >= ' ' && data[k + j] < 127) { 105 buffer.append((char) data[k + j]); 106 } else { 107 buffer.append('.'); 108 } 109 } 110 buffer.append(EOL); 111 // make explicit the dependency on the default encoding 112 stream.write(buffer.toString().getBytes(Charset.defaultCharset())); 113 stream.flush(); 114 buffer.setLength(0); 115 display_offset += chars_read; 116 } 117 } 118 119 /** 120 * The line-separator (initializes to "line.separator" system property. 121 */ 122 public static final String EOL = 123 System.getProperty("line.separator"); 124 private static final char[] _hexcodes = 125 { 126 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 127 'A', 'B', 'C', 'D', 'E', 'F' 128 }; 129 private static final int[] _shifts = 130 { 131 28, 24, 20, 16, 12, 8, 4, 0 132 }; 133 134 /** 135 * Dump a long value into a StringBuilder. 136 * 137 * @param _lbuffer the StringBuilder to dump the value in 138 * @param value the long value to be dumped 139 * @return StringBuilder containing the dumped value. 140 */ 141 private static StringBuilder dump(final StringBuilder _lbuffer, final long value) { 142 for (int j = 0; j < 8; j++) { 143 _lbuffer 144 .append(_hexcodes[(int) (value >> _shifts[j]) & 15]); 145 } 146 return _lbuffer; 147 } 148 149 /** 150 * Dump a byte value into a StringBuilder. 151 * 152 * @param _cbuffer the StringBuilder to dump the value in 153 * @param value the byte value to be dumped 154 * @return StringBuilder containing the dumped value. 155 */ 156 private static StringBuilder dump(final StringBuilder _cbuffer, final byte value) { 157 for (int j = 0; j < 2; j++) { 158 _cbuffer.append(_hexcodes[value >> _shifts[j + 6] & 15]); 159 } 160 return _cbuffer; 161 } 162 163}