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 java.io.IOException;
20  import java.io.OutputStream;
21  import java.nio.charset.Charset;
22  
23  /**
24   * Dumps data in hexadecimal format.
25   * <p>
26   * Provides a single function to take an array of bytes and display it
27   * in hexadecimal form.
28   * <p>
29   * Origin of code: POI.
30   *
31   * @version $Id: HexDump.java 1471767 2013-04-24 23:24:19Z sebb $
32   */
33  public class HexDump {
34  
35      /**
36       * Instances should NOT be constructed in standard programming.
37       */
38      public HexDump() {
39          super();
40      }
41  
42      /**
43       * Dump an array of bytes to an OutputStream. The output is formatted
44       * for human inspection, with a hexadecimal offset followed by the
45       * hexadecimal values of the next 16 bytes of data and the printable ASCII
46       * characters (if any) that those bytes represent printed per each line
47       * of output.
48       * <p>
49       * The offset argument specifies the start offset of the data array
50       * within a larger entity like a file or an incoming stream. For example,
51       * if the data array contains the third kibibyte of a file, then the
52       * offset argument should be set to 2048. The offset value printed
53       * at the beginning of each line indicates where in that larger entity
54       * the first byte on that line is located.
55       * <p>
56       * All bytes between the given index (inclusive) and the end of the
57       * data array are dumped.
58       *
59       * @param data  the byte array to be dumped
60       * @param offset  offset of the byte array within a larger entity
61       * @param stream  the OutputStream to which the data is to be
62       *               written
63       * @param index initial index into the byte array
64       *
65       * @throws IOException is thrown if anything goes wrong writing
66       *         the data to stream
67       * @throws ArrayIndexOutOfBoundsException if the index is
68       *         outside the data array's bounds
69       * @throws IllegalArgumentException if the output stream is null
70       */
71  
72      public static void dump(final byte[] data, final long offset,
73                              final OutputStream stream, final int index)
74              throws IOException, ArrayIndexOutOfBoundsException,
75              IllegalArgumentException {
76  
77          if (index < 0 || index >= data.length) {
78              throw new ArrayIndexOutOfBoundsException(
79                      "illegal index: " + index + " into array of length "
80                      + data.length);
81          }
82          if (stream == null) {
83              throw new IllegalArgumentException("cannot write to nullstream");
84          }
85          long display_offset = offset + index;
86          final StringBuilder buffer = new StringBuilder(74);
87  
88          for (int j = index; j < data.length; j += 16) {
89              int chars_read = data.length - j;
90  
91              if (chars_read > 16) {
92                  chars_read = 16;
93              }
94              dump(buffer, display_offset).append(' ');
95              for (int k = 0; k < 16; k++) {
96                  if (k < chars_read) {
97                      dump(buffer, data[k + j]);
98                  } else {
99                      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 }