    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     */
    017    package org.apache.commons.io;
    018
    019    import java.io.IOException;
    020    import java.io.OutputStream;
    021
    022    /**
    023     * Dumps data in hexadecimal format.
    024     * <p>
    025     * Provides a single function to take an array of bytes and display it
    026     * in hexadecimal form.
    027     * <p>
    028     * Origin of code: POI.
    029     *
    030     * @author Scott Sanders
    031     * @author Marc Johnson
    032     * @version $Id: HexDump.java 736507 2009-01-22 00:52:15Z jukka $
    033     */
    034    public class HexDump {
    035
    036        /**
    037         * Instances should NOT be constructed in standard programming.
    038         */
    039        public HexDump() {
    040            super();
    041        }
    042
    043        /**
    044         * Dump an array of bytes to an OutputStream. The output is formatted
    045         * for human inspection, with a hexadecimal offset followed by the
    046         * hexadecimal values of the next 16 bytes of data and the printable ASCII
    047         * characters (if any) that those bytes represent printed per each line
    048         * of output.
    049         * <p>
    050         * The offset argument specifies the start offset of the data array
    051         * within a larger entity like a file or an incoming stream. For example,
    052         * if the data array contains the third kibibyte of a file, then the
    053         * offset argument should be set to 2048. The offset value printed
    054         * at the beginning of each line indicates where in that larger entity
    055         * the first byte on that line is located.
    056         * <p>
    057         * All bytes between the given index (inclusive) and the end of the
    058         * data array are dumped.
    059         *
    060         * @param data  the byte array to be dumped
    061         * @param offset  offset of the byte array within a larger entity
    062         * @param stream  the OutputStream to which the data is to be
    063         *               written
    064         * @param index initial index into the byte array
    065         *
    066         * @throws IOException is thrown if anything goes wrong writing
    067         *         the data to stream
    068         * @throws ArrayIndexOutOfBoundsException if the index is
    069         *         outside the data array's bounds
    070         * @throws IllegalArgumentException if the output stream is null
    071         */
    072
    073        public static void dump(byte[] data, long offset,
    074                                OutputStream stream, int index)
    075                throws IOException, ArrayIndexOutOfBoundsException,
    076                IllegalArgumentException {
    077
    078            if ((index < 0) || (index >= data.length)) {
    079                throw new ArrayIndexOutOfBoundsException(
    080                        "illegal index: " + index + " into array of length "
    081                        + data.length);
    082            }
    083            if (stream == null) {
    084                throw new IllegalArgumentException("cannot write to nullstream");
    085            }
    086            long display_offset = offset + index;
    087            StringBuilder buffer = new StringBuilder(74);
    088
    089            for (int j = index; j < data.length; j += 16) {
    090                int chars_read = data.length - j;
    091
    092                if (chars_read > 16) {
    093                    chars_read = 16;
    094                }
    095                dump(buffer, display_offset).append(' ');
    096                for (int k = 0; k < 16; k++) {
    097                    if (k < chars_read) {
    098                        dump(buffer, data[k + j]);
    099                    } else {
    100                        buffer.append("  ");
    101                    }
    102                    buffer.append(' ');
    103                }
    104                for (int k = 0; k < chars_read; k++) {
    105                    if ((data[k + j] >= ' ') && (data[k + j] < 127)) {
    106                        buffer.append((char) data[k + j]);
    107                    } else {
    108                        buffer.append('.');
    109                    }
    110                }
    111                buffer.append(EOL);
    112                stream.write(buffer.toString().getBytes());
    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(StringBuilder _lbuffer, 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(StringBuilder _cbuffer, 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    }